Bug 1063066 - [Flame][KK] Bluetooth transfer does not work in Gallery App. r=tzimmermann
authorBen Tian <btian@mozilla.com>
Thu, 25 Sep 2014 18:35:54 +0800
changeset 231841 15aae506ef734ba0f28475cc7e9e025df6e96a06
parent 231840 f35fc2502cb841be7a30211e451e79bdc107da04
child 231842 6b2bd6111568d249cdd0163f06b856d8a404a8f4
push idunknown
push userunknown
push dateunknown
reviewerstzimmermann
bugs1063066
milestone35.0a1
Bug 1063066 - [Flame][KK] Bluetooth transfer does not work in Gallery App. r=tzimmermann
dom/bluetooth/BluetoothInterface.h
dom/bluetooth/bluedroid/BluetoothSocket.cpp
dom/bluetooth/bluedroid/BluetoothSocket.h
dom/bluetooth/bluedroid/BluetoothSocketHALInterface.cpp
dom/bluetooth/bluedroid/BluetoothSocketHALInterface.h
dom/bluetooth2/BluetoothInterface.h
dom/bluetooth2/BluetoothRilListener.cpp
dom/bluetooth2/bluedroid/BluetoothSocket.cpp
dom/bluetooth2/bluedroid/BluetoothSocket.h
dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.cpp
dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.h
--- a/dom/bluetooth/BluetoothInterface.h
+++ b/dom/bluetooth/BluetoothInterface.h
@@ -49,16 +49,18 @@ public:
   virtual void Connect(const nsAString& aBdAddr,
                        BluetoothSocketType aType,
                        const uint8_t aUuid[16],
                        int aChannel, bool aEncrypt, bool aAuth,
                        BluetoothSocketResultHandler* aRes) = 0;
 
   virtual void Accept(int aFd, BluetoothSocketResultHandler* aRes) = 0;
 
+  virtual void Close(BluetoothSocketResultHandler* aRes) = 0;
+
 protected:
   virtual ~BluetoothSocketInterface();
 };
 
 //
 // Handsfree Interface
 //
 
--- a/dom/bluetooth/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.cpp
@@ -390,17 +390,20 @@ public:
   , mFd(aFd)
   { }
 
   NS_IMETHOD Run() MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(sBluetoothSocketInterface);
 
-    sBluetoothSocketInterface->Accept(mFd, new AcceptResultHandler(GetIO()));
+    BluetoothSocketResultHandler* res = new AcceptResultHandler(GetIO());
+    GetIO()->mConsumer->SetCurrentResultHandler(res);
+
+    sBluetoothSocketInterface->Accept(mFd, res);
 
     return NS_OK;
   }
 
 private:
   int mFd;
 };
 
@@ -471,16 +474,17 @@ DroidSocketImpl::OnSocketCanConnectWitho
   }
 }
 
 BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
                                  BluetoothSocketType aType,
                                  bool aAuth,
                                  bool aEncrypt)
   : mObserver(aObserver)
+  , mCurrentRes(nullptr)
   , mImpl(nullptr)
   , mAuth(aAuth)
   , mEncrypt(aEncrypt)
 {
   MOZ_ASSERT(aObserver);
 
   EnsureBluetoothSocketHalLoad();
   mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
@@ -522,23 +526,25 @@ BluetoothSocket::ConnectSocket(const nsA
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_FALSE(mImpl, false);
 
   SetConnectionStatus(SOCKET_CONNECTING);
 
   mImpl = new DroidSocketImpl(XRE_GetIOMessageLoop(), this);
 
+  BluetoothSocketResultHandler* res = new ConnectSocketResultHandler(mImpl);
+  SetCurrentResultHandler(res);
+
   // TODO: uuid as argument
   sBluetoothSocketInterface->Connect(
     aDeviceAddress,
     BluetoothSocketType::RFCOMM,
     UUID_OBEX_OBJECT_PUSH,
-    aChannel, mEncrypt, mAuth,
-    new ConnectSocketResultHandler(mImpl));
+    aChannel, mEncrypt, mAuth, res);
 
   return true;
 }
 
 class ListenResultHandler MOZ_FINAL : public BluetoothSocketResultHandler
 {
 public:
   ListenResultHandler(DroidSocketImpl* aImpl)
@@ -571,34 +577,42 @@ BluetoothSocket::ListenSocket(int aChann
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_FALSE(mImpl, false);
 
   SetConnectionStatus(SOCKET_LISTENING);
 
   mImpl = new DroidSocketImpl(XRE_GetIOMessageLoop(), this);
 
+  BluetoothSocketResultHandler* res = new ListenResultHandler(mImpl);
+  SetCurrentResultHandler(res);
+
   sBluetoothSocketInterface->Listen(
     BluetoothSocketType::RFCOMM,
     NS_LITERAL_STRING("OBEX Object Push"),
     UUID_OBEX_OBJECT_PUSH,
-    aChannel, mEncrypt, mAuth,
-    new ListenResultHandler(mImpl));
+    aChannel, mEncrypt, mAuth, res);
 
   return true;
 }
 
 void
 BluetoothSocket::CloseSocket()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(sBluetoothSocketInterface);
   if (!mImpl) {
     return;
   }
 
+  // Stop any watching |SocketMessageWatcher|
+  if (mCurrentRes) {
+    sBluetoothSocketInterface->Close(mCurrentRes);
+  }
+
   // From this point on, we consider mImpl as being deleted.
   // We sever the relationship here so any future calls to listen or connect
   // will create a new implementation.
   mImpl->ShutdownOnMainThread();
   XRE_GetIOMessageLoop()->PostTask(
     FROM_HERE, new SocketIOShutdownTask<DroidSocketImpl>(mImpl));
 
   mImpl = nullptr;
@@ -628,24 +642,28 @@ BluetoothSocket::ReceiveSocketData(nsAut
   mObserver->ReceiveSocketData(this, aMessage);
 }
 
 void
 BluetoothSocket::OnConnectSuccess()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
+
+  SetCurrentResultHandler(nullptr);
   mObserver->OnSocketConnectSuccess(this);
 }
 
 void
 BluetoothSocket::OnConnectError()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
+
+  SetCurrentResultHandler(nullptr);
   mObserver->OnSocketConnectError(this);
 }
 
 void
 BluetoothSocket::OnDisconnect()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
--- a/dom/bluetooth/bluedroid/BluetoothSocket.h
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_bluetooth_BluetoothSocket_h
 
 #include "BluetoothCommon.h"
 #include "mozilla/ipc/SocketBase.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocketObserver;
+class BluetoothSocketResultHandler;
 class DroidSocketImpl;
 
 class BluetoothSocket : public mozilla::ipc::SocketConsumerBase
 {
 public:
   BluetoothSocket(BluetoothSocketObserver* aObserver,
                   BluetoothSocketType aType,
                   bool aAuth,
@@ -42,18 +43,24 @@ public:
     aDeviceAddress = mDeviceAddress;
   }
 
   inline void SetAddress(const nsAString& aDeviceAddress)
   {
     mDeviceAddress = aDeviceAddress;
   }
 
+  inline void SetCurrentResultHandler(BluetoothSocketResultHandler* aRes)
+  {
+    mCurrentRes = aRes;
+  }
+
 private:
   BluetoothSocketObserver* mObserver;
+  BluetoothSocketResultHandler* mCurrentRes;
   DroidSocketImpl* mImpl;
   nsString mDeviceAddress;
   bool mAuth;
   bool mEncrypt;
 };
 
 END_BLUETOOTH_NAMESPACE
 
--- a/dom/bluetooth/bluedroid/BluetoothSocketHALInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothSocketHALInterface.cpp
@@ -4,34 +4,35 @@
  * 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 "nsClassHashtable.h"
 #include "nsXULAppAPI.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 typedef
   BluetoothHALInterfaceRunnable1<BluetoothSocketResultHandler, void,
-                                       int, int>
+                                 int, int>
   BluetoothSocketHALIntResultRunnable;
 
 typedef
   BluetoothHALInterfaceRunnable3<BluetoothSocketResultHandler, void,
-                                       int, const nsString, int,
-                                       int, const nsAString_internal&, int>
+                                 int, const nsString, int,
+                                 int, const nsAString_internal&, int>
   BluetoothSocketHALIntStringIntResultRunnable;
 
 typedef
   BluetoothHALInterfaceRunnable1<BluetoothSocketResultHandler, void,
-                                       BluetoothStatus, BluetoothStatus>
+                                 BluetoothStatus, BluetoothStatus>
   BluetoothSocketHALErrorRunnable;
 
 static nsresult
 DispatchBluetoothSocketHALResult(
   BluetoothSocketResultHandler* aRes,
   void (BluetoothSocketResultHandler::*aMethod)(int), int aArg,
   BluetoothStatus aStatus)
 {
@@ -73,21 +74,21 @@ DispatchBluetoothSocketHALResult(
   if (NS_FAILED(rv)) {
     BT_WARNING("NS_DispatchToMainThread failed: %X", rv);
   }
   return rv;
 }
 
 void
 BluetoothSocketHALInterface::Listen(BluetoothSocketType aType,
-                                          const nsAString& aServiceName,
-                                          const uint8_t aServiceUuid[16],
-                                          int aChannel, bool aEncrypt,
-                                          bool aAuth,
-                                          BluetoothSocketResultHandler* aRes)
+                                    const nsAString& aServiceName,
+                                    const uint8_t aServiceUuid[16],
+                                    int aChannel, bool aEncrypt,
+                                    bool aAuth,
+                                    BluetoothSocketResultHandler* aRes)
 {
   int fd;
   bt_status_t status;
   btsock_type_t type = BTSOCK_RFCOMM; // silences compiler warning
 
   if (NS_SUCCEEDED(Convert(aType, type))) {
     status = mInterface->listen(type,
                                 NS_ConvertUTF16toUTF8(aServiceName).get(),
@@ -104,16 +105,44 @@ BluetoothSocketHALInterface::Listen(Blue
       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]
@@ -130,21 +159,24 @@ public:
   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)
+  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
   {
@@ -159,34 +191,45 @@ public:
         break;
       default:
         /* message-size error */
         status = STATUS_FAIL;
         break;
     }
 
     if (IsComplete() || status != STATUS_SUCCESS) {
-      mWatcher.StopWatchingFileDescriptor();
+      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;
@@ -219,16 +262,21 @@ public:
     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;
 
@@ -317,16 +365,17 @@ private:
     }
   }
 
   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:
@@ -368,42 +417,37 @@ private:
  * 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)
+  : SocketMessageWatcher(aFd, aRes)
   { }
 
   void Proceed(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
-    if (mRes) {
-      DispatchBluetoothSocketHALResult(
-        mRes, &BluetoothSocketResultHandler::Connect, GetFd(),
-        GetBdAddress(), GetConnectionStatus(), aStatus);
-    }
+    DispatchBluetoothSocketHALResult(
+      GetResultHandler(), &BluetoothSocketResultHandler::Connect,
+      GetFd(), GetBdAddress(), GetConnectionStatus(), aStatus);
+
     MessageLoopForIO::current()->PostTask(
       FROM_HERE, new DeleteTask<ConnectWatcher>(this));
   }
-
-private:
-  nsRefPtr<BluetoothSocketResultHandler> mRes;
 };
 
 void
 BluetoothSocketHALInterface::Connect(const nsAString& aBdAddr,
-                                           BluetoothSocketType aType,
-                                           const uint8_t aUuid[16],
-                                           int aChannel, bool aEncrypt,
-                                           bool aAuth,
-                                           BluetoothSocketResultHandler* aRes)
+                                     BluetoothSocketType aType,
+                                     const uint8_t aUuid[16],
+                                     int aChannel, bool aEncrypt,
+                                     bool aAuth,
+                                     BluetoothSocketResultHandler* aRes)
 {
   int fd;
   bt_status_t status;
   bt_bdaddr_t bdAddr;
   btsock_type_t type = BTSOCK_RFCOMM; // silences compiler warning
 
   if (NS_SUCCEEDED(Convert(aBdAddr, bdAddr)) &&
       NS_SUCCEEDED(Convert(aType, type))) {
@@ -420,58 +464,90 @@ BluetoothSocketHALInterface::Connect(con
     XRE_GetIOMessageLoop()->PostTask(FROM_HERE, t);
   } else if (aRes) {
     DispatchBluetoothSocketHALResult(
       aRes, &BluetoothSocketResultHandler::Connect, -1, EmptyString(), 0,
       ConvertDefault(status, STATUS_FAIL));
   }
 }
 
-/* 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
+/* |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
 {
 public:
   AcceptWatcher(int aFd, BluetoothSocketResultHandler* aRes)
-  : SocketMessageWatcher(aFd)
-  , mRes(aRes)
-  {
-    /* not supplying a result handler leaks received file descriptor */
-    MOZ_ASSERT(mRes);
-  }
+  : SocketMessageWatcher(aFd, aRes)
+  { }
 
   void Proceed(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
-    if (mRes) {
-      DispatchBluetoothSocketHALResult(
-        mRes, &BluetoothSocketResultHandler::Accept, GetClientFd(),
-        GetBdAddress(), GetConnectionStatus(), aStatus);
-    }
+    DispatchBluetoothSocketHALResult(
+      GetResultHandler(), &BluetoothSocketResultHandler::Accept,
+      GetClientFd(), GetBdAddress(), GetConnectionStatus(), aStatus);
+
     MessageLoopForIO::current()->PostTask(
       FROM_HERE, new DeleteTask<AcceptWatcher>(this));
   }
-
-private:
-  nsRefPtr<BluetoothSocketResultHandler> mRes;
 };
 
 void
 BluetoothSocketHALInterface::Accept(int aFd,
-                                          BluetoothSocketResultHandler* aRes)
+                                    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);
+}
+
 BluetoothSocketHALInterface::BluetoothSocketHALInterface(
   const btsock_interface_t* aInterface)
 : mInterface(aInterface)
 {
   MOZ_ASSERT(mInterface);
 }
 
 BluetoothSocketHALInterface::~BluetoothSocketHALInterface()
--- a/dom/bluetooth/bluedroid/BluetoothSocketHALInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothSocketHALInterface.h
@@ -31,16 +31,18 @@ public:
   void Connect(const nsAString& aBdAddr,
                BluetoothSocketType aType,
                const uint8_t aUuid[16],
                int aChannel, bool aEncrypt, bool aAuth,
                BluetoothSocketResultHandler* aRes);
 
   void Accept(int aFd, BluetoothSocketResultHandler* aRes);
 
+  void Close(BluetoothSocketResultHandler* aRes);
+
 protected:
   BluetoothSocketHALInterface(const btsock_interface_t* aInterface);
   ~BluetoothSocketHALInterface();
 
 private:
   const btsock_interface_t* mInterface;
 };
 
--- a/dom/bluetooth2/BluetoothInterface.h
+++ b/dom/bluetooth2/BluetoothInterface.h
@@ -49,16 +49,18 @@ public:
   virtual void Connect(const nsAString& aBdAddr,
                        BluetoothSocketType aType,
                        const uint8_t aUuid[16],
                        int aChannel, bool aEncrypt, bool aAuth,
                        BluetoothSocketResultHandler* aRes) = 0;
 
   virtual void Accept(int aFd, BluetoothSocketResultHandler* aRes) = 0;
 
+  virtual void Close(BluetoothSocketResultHandler* aRes) = 0;
+
 protected:
   virtual ~BluetoothSocketInterface();
 };
 
 //
 // Handsfree Interface
 //
 
--- a/dom/bluetooth2/BluetoothRilListener.cpp
+++ b/dom/bluetooth2/BluetoothRilListener.cpp
@@ -177,17 +177,17 @@ MobileConnectionListener::NotifyNetworkS
 bool
 MobileConnectionListener::Listen(bool aStart)
 {
   nsCOMPtr<nsIMobileConnectionService> service =
     do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(service, false);
 
   nsCOMPtr<nsIMobileConnection> connection;
-  mcService->GetItemByServiceId(mClientId, getter_AddRefs(connection));
+  service->GetItemByServiceId(mClientId, getter_AddRefs(connection));
   NS_ENSURE_TRUE(connection, false);
 
   nsresult rv;
   if (aStart) {
     rv = connection->RegisterListener(this);
   } else {
     rv = connection->UnregisterListener(this);
   }
--- a/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
@@ -390,17 +390,20 @@ public:
   , mFd(aFd)
   { }
 
   NS_IMETHOD Run() MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(sBluetoothSocketInterface);
 
-    sBluetoothSocketInterface->Accept(mFd, new AcceptResultHandler(GetIO()));
+    BluetoothSocketResultHandler* res = new AcceptResultHandler(GetIO());
+    GetIO()->mConsumer->SetCurrentResultHandler(res);
+
+    sBluetoothSocketInterface->Accept(mFd, res);
 
     return NS_OK;
   }
 
 private:
   int mFd;
 };
 
@@ -471,16 +474,17 @@ DroidSocketImpl::OnSocketCanConnectWitho
   }
 }
 
 BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
                                  BluetoothSocketType aType,
                                  bool aAuth,
                                  bool aEncrypt)
   : mObserver(aObserver)
+  , mCurrentRes(nullptr)
   , mImpl(nullptr)
   , mAuth(aAuth)
   , mEncrypt(aEncrypt)
 {
   MOZ_ASSERT(aObserver);
 
   EnsureBluetoothSocketHalLoad();
   mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
@@ -522,23 +526,25 @@ BluetoothSocket::ConnectSocket(const nsA
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_FALSE(mImpl, false);
 
   SetConnectionStatus(SOCKET_CONNECTING);
 
   mImpl = new DroidSocketImpl(XRE_GetIOMessageLoop(), this);
 
+  BluetoothSocketResultHandler* res = new ConnectSocketResultHandler(mImpl);
+  SetCurrentResultHandler(res);
+
   // TODO: uuid as argument
   sBluetoothSocketInterface->Connect(
     aDeviceAddress,
     BluetoothSocketType::RFCOMM,
     UUID_OBEX_OBJECT_PUSH,
-    aChannel, mEncrypt, mAuth,
-    new ConnectSocketResultHandler(mImpl));
+    aChannel, mEncrypt, mAuth, res);
 
   return true;
 }
 
 class ListenResultHandler MOZ_FINAL : public BluetoothSocketResultHandler
 {
 public:
   ListenResultHandler(DroidSocketImpl* aImpl)
@@ -571,34 +577,42 @@ BluetoothSocket::ListenSocket(int aChann
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_FALSE(mImpl, false);
 
   SetConnectionStatus(SOCKET_LISTENING);
 
   mImpl = new DroidSocketImpl(XRE_GetIOMessageLoop(), this);
 
+  BluetoothSocketResultHandler* res = new ListenResultHandler(mImpl);
+  SetCurrentResultHandler(res);
+
   sBluetoothSocketInterface->Listen(
     BluetoothSocketType::RFCOMM,
     NS_LITERAL_STRING("OBEX Object Push"),
     UUID_OBEX_OBJECT_PUSH,
-    aChannel, mEncrypt, mAuth,
-    new ListenResultHandler(mImpl));
+    aChannel, mEncrypt, mAuth, res);
 
   return true;
 }
 
 void
 BluetoothSocket::CloseSocket()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(sBluetoothSocketInterface);
   if (!mImpl) {
     return;
   }
 
+  // Stop any watching |SocketMessageWatcher|
+  if (mCurrentRes) {
+    sBluetoothSocketInterface->Close(mCurrentRes);
+  }
+
   // From this point on, we consider mImpl as being deleted.
   // We sever the relationship here so any future calls to listen or connect
   // will create a new implementation.
   mImpl->ShutdownOnMainThread();
   XRE_GetIOMessageLoop()->PostTask(
     FROM_HERE, new SocketIOShutdownTask<DroidSocketImpl>(mImpl));
 
   mImpl = nullptr;
@@ -628,24 +642,28 @@ BluetoothSocket::ReceiveSocketData(nsAut
   mObserver->ReceiveSocketData(this, aMessage);
 }
 
 void
 BluetoothSocket::OnConnectSuccess()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
+
+  SetCurrentResultHandler(nullptr);
   mObserver->OnSocketConnectSuccess(this);
 }
 
 void
 BluetoothSocket::OnConnectError()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
+
+  SetCurrentResultHandler(nullptr);
   mObserver->OnSocketConnectError(this);
 }
 
 void
 BluetoothSocket::OnDisconnect()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
--- a/dom/bluetooth2/bluedroid/BluetoothSocket.h
+++ b/dom/bluetooth2/bluedroid/BluetoothSocket.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_bluetooth_BluetoothSocket_h
 
 #include "BluetoothCommon.h"
 #include "mozilla/ipc/SocketBase.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocketObserver;
+class BluetoothSocketResultHandler;
 class DroidSocketImpl;
 
 class BluetoothSocket : public mozilla::ipc::SocketConsumerBase
 {
 public:
   BluetoothSocket(BluetoothSocketObserver* aObserver,
                   BluetoothSocketType aType,
                   bool aAuth,
@@ -42,18 +43,24 @@ public:
     aDeviceAddress = mDeviceAddress;
   }
 
   inline void SetAddress(const nsAString& aDeviceAddress)
   {
     mDeviceAddress = aDeviceAddress;
   }
 
+  inline void SetCurrentResultHandler(BluetoothSocketResultHandler* aRes)
+  {
+    mCurrentRes = aRes;
+  }
+
 private:
   BluetoothSocketObserver* mObserver;
+  BluetoothSocketResultHandler* mCurrentRes;
   DroidSocketImpl* mImpl;
   nsString mDeviceAddress;
   bool mAuth;
   bool mEncrypt;
 };
 
 END_BLUETOOTH_NAMESPACE
 
--- a/dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.cpp
@@ -4,16 +4,17 @@
  * 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 "nsClassHashtable.h"
 #include "nsXULAppAPI.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 typedef
   BluetoothHALInterfaceRunnable1<BluetoothSocketResultHandler, void,
                                  int, int>
   BluetoothSocketHALIntResultRunnable;
@@ -104,16 +105,44 @@ BluetoothSocketHALInterface::Listen(Blue
       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]
@@ -130,21 +159,24 @@ public:
   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)
+  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
   {
@@ -159,34 +191,45 @@ public:
         break;
       default:
         /* message-size error */
         status = STATUS_FAIL;
         break;
     }
 
     if (IsComplete() || status != STATUS_SUCCESS) {
-      mWatcher.StopWatchingFileDescriptor();
+      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;
@@ -219,16 +262,21 @@ public:
     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;
 
@@ -317,16 +365,17 @@ private:
     }
   }
 
   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:
@@ -368,33 +417,28 @@ private:
  * 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)
+  : SocketMessageWatcher(aFd, aRes)
   { }
 
   void Proceed(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
-    if (mRes) {
-      DispatchBluetoothSocketHALResult(
-        mRes, &BluetoothSocketResultHandler::Connect, GetFd(),
-        GetBdAddress(), GetConnectionStatus(), aStatus);
-    }
+    DispatchBluetoothSocketHALResult(
+      GetResultHandler(), &BluetoothSocketResultHandler::Connect,
+      GetFd(), GetBdAddress(), GetConnectionStatus(), aStatus);
+
     MessageLoopForIO::current()->PostTask(
       FROM_HERE, new DeleteTask<ConnectWatcher>(this));
   }
-
-private:
-  nsRefPtr<BluetoothSocketResultHandler> mRes;
 };
 
 void
 BluetoothSocketHALInterface::Connect(const nsAString& aBdAddr,
                                      BluetoothSocketType aType,
                                      const uint8_t aUuid[16],
                                      int aChannel, bool aEncrypt,
                                      bool aAuth,
@@ -431,47 +475,79 @@ BluetoothSocketHALInterface::Connect(con
  * 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
 {
 public:
   AcceptWatcher(int aFd, BluetoothSocketResultHandler* aRes)
-  : SocketMessageWatcher(aFd)
-  , mRes(aRes)
-  {
-    /* not supplying a result handler leaks received file descriptor */
-    MOZ_ASSERT(mRes);
-  }
+  : SocketMessageWatcher(aFd, aRes)
+  { }
 
   void Proceed(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
-    if (mRes) {
-      DispatchBluetoothSocketHALResult(
-        mRes, &BluetoothSocketResultHandler::Accept, GetClientFd(),
-        GetBdAddress(), GetConnectionStatus(), aStatus);
-    }
+    DispatchBluetoothSocketHALResult(
+      GetResultHandler(), &BluetoothSocketResultHandler::Accept,
+      GetClientFd(), GetBdAddress(), GetConnectionStatus(), aStatus);
+
     MessageLoopForIO::current()->PostTask(
       FROM_HERE, new DeleteTask<AcceptWatcher>(this));
   }
-
-private:
-  nsRefPtr<BluetoothSocketResultHandler> mRes;
 };
 
 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);
+}
+
 BluetoothSocketHALInterface::BluetoothSocketHALInterface(
   const btsock_interface_t* aInterface)
 : mInterface(aInterface)
 {
   MOZ_ASSERT(mInterface);
 }
 
 BluetoothSocketHALInterface::~BluetoothSocketHALInterface()
--- a/dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.h
+++ b/dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.h
@@ -31,16 +31,18 @@ public:
   void Connect(const nsAString& aBdAddr,
                BluetoothSocketType aType,
                const uint8_t aUuid[16],
                int aChannel, bool aEncrypt, bool aAuth,
                BluetoothSocketResultHandler* aRes);
 
   void Accept(int aFd, BluetoothSocketResultHandler* aRes);
 
+  void Close(BluetoothSocketResultHandler* aRes);
+
 protected:
   BluetoothSocketHALInterface(const btsock_interface_t* aInterface);
   ~BluetoothSocketHALInterface();
 
 private:
   const btsock_interface_t* mInterface;
 };