Bug 915533 - Patch 2/2: [bludroid OPP] BluetoothSocket, r=echou
☠☠ backed out by 9523707a0003 ☠ ☠
authorBen Tian <btian@mozilla.com>
Mon, 02 Dec 2013 10:39:58 +0800
changeset 158256 22ad4bbd595d5225840ca38cabab1f6fa764bb7d
parent 158255 2faed81ad05fedbd555859859ed16e91607a09c5
child 158257 1378d9a9845a1f641ffecbc4cc8608c325a4bf94
push id25739
push usercbook@mozilla.com
push dateMon, 02 Dec 2013 11:47:10 +0000
treeherdermozilla-central@2581b84e0ca1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersechou
bugs915533
milestone28.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 915533 - Patch 2/2: [bludroid OPP] BluetoothSocket, r=echou
dom/bluetooth/bluedroid/BluetoothOppManager.cpp
dom/bluetooth/bluedroid/BluetoothOppManager.h
dom/bluetooth/bluedroid/BluetoothSocket.cpp
dom/bluetooth/bluedroid/BluetoothSocket.h
dom/bluetooth/bluedroid/gonk/BluetoothServiceBluedroid.cpp
dom/bluetooth/bluez/linux/BluetoothDBusService.cpp
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
@@ -153,21 +153,17 @@ public:
   CloseSocketTask(BluetoothSocket* aSocket) : mSocket(aSocket)
   {
     MOZ_ASSERT(aSocket);
   }
 
   void Run() MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
-
-    if (mSocket->GetConnectionStatus() ==
-        SocketConnectionStatus::SOCKET_CONNECTED) {
-      mSocket->Disconnect();
-    }
+    mSocket->CloseDroidSocket();
   }
 
 private:
   nsRefPtr<BluetoothSocket> mSocket;
 };
 
 BluetoothOppManager::BluetoothOppManager() : mConnected(false)
                                            , mRemoteObexVersion(0)
@@ -184,17 +180,17 @@ BluetoothOppManager::BluetoothOppManager
                                            , mSuccessFlag(false)
                                            , mIsServer(true)
                                            , mWaitingForConfirmationFlag(false)
                                            , mFileLength(0)
                                            , mSentFileLength(0)
                                            , mWaitingToSendPutFinal(false)
                                            , mCurrentBlobIndex(-1)
 {
-  mConnectedDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
+  mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
 }
 
 BluetoothOppManager::~BluetoothOppManager()
 {
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_ENSURE_TRUE_VOID(obs);
   if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
     BT_WARNING("Failed to remove shutdown observer!");
@@ -206,17 +202,24 @@ BluetoothOppManager::Init()
 {
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_ENSURE_TRUE(obs, false);
   if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
     BT_WARNING("Failed to add shutdown observer!");
     return false;
   }
 
-  Listen();
+  /**
+   * We don't start listening here as BluetoothServiceBluedroid calls Listen()
+   * immediately when BT stops.
+   *
+   * If we start listening here, the listening fails when device boots up since
+   * Listen() is called again and restarts server socket. The restart causes
+   * absence of read events when device boots up.
+   */
 
   return true;
 }
 
 //static
 BluetoothOppManager*
 BluetoothOppManager::Get()
 {
@@ -239,101 +242,78 @@ BluetoothOppManager::Get()
 }
 
 void
 BluetoothOppManager::ConnectInternal(const nsAString& aDeviceAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Stop listening because currently we only support one connection at a time.
-  if (mRfcommSocket) {
-    mRfcommSocket->Disconnect();
-    mRfcommSocket = nullptr;
-  }
-
-  if (mL2capSocket) {
-    mL2capSocket->Disconnect();
-    mL2capSocket = nullptr;
+  if (mServerSocket) {
+    mServerSocket->Disconnect();
+    mServerSocket = nullptr;
   }
 
   mIsServer = false;
 
   BluetoothService* bs = BluetoothService::Get();
   if (!bs || sInShutdown || mSocket) {
     OnSocketConnectError(mSocket);
     return;
   }
 
-  mNeedsUpdatingSdpRecords = true;
-
-  nsString uuid;
-  BluetoothUuidHelper::GetString(BluetoothServiceClass::OBJECT_PUSH, uuid);
-
-  if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, uuid, this))) {
-    OnSocketConnectError(mSocket);
-    return;
-  }
-
   mSocket =
     new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
+  mSocket->Connect(aDeviceAddress, -1);
 }
 
 void
 BluetoothOppManager::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   sInShutdown = true;
 
   if (mSocket) {
     mSocket->Disconnect();
     mSocket = nullptr;
   }
-  if (mRfcommSocket) {
-    mRfcommSocket->Disconnect();
-    mRfcommSocket = nullptr;
-  }
-  if (mL2capSocket) {
-    mL2capSocket->Disconnect();
-    mL2capSocket = nullptr;
+  if (mServerSocket) {
+    mServerSocket->Disconnect();
+    mServerSocket = nullptr;
   }
   sBluetoothOppManager = nullptr;
 }
 
 bool
 BluetoothOppManager::Listen()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mSocket) {
     BT_WARNING("mSocket exists. Failed to listen.");
     return false;
   }
 
-  if (!mRfcommSocket) {
-    mRfcommSocket =
-      new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
-
-    if (!mRfcommSocket->Listen(BluetoothReservedChannels::CHANNEL_OPUSH)) {
-      BT_WARNING("[OPP] Can't listen on RFCOMM socket!");
-      mRfcommSocket = nullptr;
-      return false;
-    }
+  /**
+   * Restart server socket since its underlying fd becomes invalid when
+   * BT stops; otherwise no more read events would be received even if
+   * BT restarts.
+   */
+  if (mServerSocket) {
+    mServerSocket->Disconnect();
+    mServerSocket = nullptr;
   }
 
-  if (!mL2capSocket) {
-    mL2capSocket =
-      new BluetoothSocket(this, BluetoothSocketType::EL2CAP, true, true);
+  mServerSocket =
+    new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
 
-    if (!mL2capSocket->Listen(BluetoothReservedChannels::CHANNEL_OPUSH_L2CAP)) {
-      BT_WARNING("[OPP] Can't listen on L2CAP socket!");
-      mRfcommSocket->Disconnect();
-      mRfcommSocket = nullptr;
-      mL2capSocket = nullptr;
-      return false;
-    }
+  if (!mServerSocket->Listen(BluetoothReservedChannels::CHANNEL_OPUSH)) {
+    BT_WARNING("[OPP] Can't listen on RFCOMM socket!");
+    mServerSocket = nullptr;
+    return false;
   }
 
   mIsServer = true;
 
   return true;
 }
 
 void
@@ -1245,33 +1225,33 @@ BluetoothOppManager::SendObexData(uint8_
   SetObexPacketInfo(aData, aOpcode, aSize);
 
   if (!mIsServer) {
     mLastCommand = aOpcode;
   }
 
   UnixSocketRawData* s = new UnixSocketRawData(aSize);
   memcpy(s->mData, aData, s->mSize);
-  mSocket->SendSocketData(s);
+  mSocket->SendDroidSocketData(s);
 }
 
 void
 BluetoothOppManager::FileTransferComplete()
 {
   if (mSendTransferCompleteFlag) {
     return;
   }
 
   nsString type, name;
   BluetoothValue v;
   InfallibleTArray<BluetoothNamedValue> parameters;
   type.AssignLiteral("bluetooth-opp-transfer-complete");
 
   name.AssignLiteral("address");
-  v = mConnectedDeviceAddress;
+  v = mDeviceAddress;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   name.AssignLiteral("success");
   v = mSuccessFlag;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   name.AssignLiteral("received");
   v = mIsServer;
@@ -1301,17 +1281,17 @@ void
 BluetoothOppManager::StartFileTransfer()
 {
   nsString type, name;
   BluetoothValue v;
   InfallibleTArray<BluetoothNamedValue> parameters;
   type.AssignLiteral("bluetooth-opp-transfer-start");
 
   name.AssignLiteral("address");
-  v = mConnectedDeviceAddress;
+  v = mDeviceAddress;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   name.AssignLiteral("received");
   v = mIsServer;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   name.AssignLiteral("fileName");
   v = mFileName;
@@ -1337,17 +1317,17 @@ void
 BluetoothOppManager::UpdateProgress()
 {
   nsString type, name;
   BluetoothValue v;
   InfallibleTArray<BluetoothNamedValue> parameters;
   type.AssignLiteral("bluetooth-opp-update-progress");
 
   name.AssignLiteral("address");
-  v = mConnectedDeviceAddress;
+  v = mDeviceAddress;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   name.AssignLiteral("received");
   v = mIsServer;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   name.AssignLiteral("processedLength");
   v = mSentFileLength;
@@ -1367,17 +1347,17 @@ void
 BluetoothOppManager::ReceivingFileConfirmation()
 {
   nsString type, name;
   BluetoothValue v;
   InfallibleTArray<BluetoothNamedValue> parameters;
   type.AssignLiteral("bluetooth-opp-receiving-file-confirmation");
 
   name.AssignLiteral("address");
-  v = mConnectedDeviceAddress;
+  v = mDeviceAddress;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   name.AssignLiteral("fileName");
   v = mFileName;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   name.AssignLiteral("fileLength");
   v = mFileLength;
@@ -1407,75 +1387,64 @@ BluetoothOppManager::NotifyAboutFileChan
 
 void
 BluetoothOppManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
 {
   BT_LOGR("%s: [%s]", __FUNCTION__, (mIsServer)? "server" : "client");
   MOZ_ASSERT(aSocket);
 
   /**
-   * If the created connection is an inbound connection, close another server
-   * socket because currently only one file-transfer session is allowed. After
-   * that, we need to make sure that both server socket would be nulled out.
+   * If the created connection is an inbound connection, close server socket
+   * because currently only one file-transfer session is allowed. After that,
+   * we need to make sure that server socket would be nulled out.
    * As for outbound connections, we just notify the controller that it's done.
    */
-  if (aSocket == mRfcommSocket) {
+  if (aSocket == mServerSocket) {
     MOZ_ASSERT(!mSocket);
-    mRfcommSocket.swap(mSocket);
-
-    mL2capSocket->Disconnect();
-    mL2capSocket = nullptr;
-  } else if (aSocket == mL2capSocket) {
-    MOZ_ASSERT(!mSocket);
-    mL2capSocket.swap(mSocket);
-
-    mRfcommSocket->Disconnect();
-    mRfcommSocket = nullptr;
+    mServerSocket.swap(mSocket);
   }
 
   // Cache device address since we can't get socket address when a remote
   // device disconnect with us.
-  mSocket->GetAddress(mConnectedDeviceAddress);
+  mSocket->GetAddress(mDeviceAddress);
 
   // Start sending file if we connect as a client
   if (!mIsServer) {
     StartSendingNextFile();
   }
 }
 
 void
 BluetoothOppManager::OnSocketConnectError(BluetoothSocket* aSocket)
 {
   BT_LOGR("%s: [%s]", __FUNCTION__, (mIsServer)? "server" : "client");
 
-  mRfcommSocket = nullptr;
-  mL2capSocket = nullptr;
+  mServerSocket = nullptr;
   mSocket = nullptr;
 
   if (!mIsServer) {
     // Inform gaia of remaining blobs' sending failure
     DiscardBlobsToSend();
   }
 
   // Listen as a server if there's no more batch to process
   if (!ProcessNextBatch()) {
     Listen();
   }
 }
 
 void
 BluetoothOppManager::OnSocketDisconnect(BluetoothSocket* aSocket)
 {
-  BT_LOGR("%s: [%s]", __FUNCTION__, (mIsServer)? "server" : "client");
   MOZ_ASSERT(aSocket);
-
   if (aSocket != mSocket) {
     // Do nothing when a listening server socket is closed.
     return;
   }
+  BT_LOGR("%s: [%s]", __FUNCTION__, (mIsServer) ? "client" : "server");
 
   /**
    * It is valid for a bluetooth device which is transfering file via OPP
    * closing socket without sending OBEX disconnect request first. So we
    * delete the broken file when we failed to receive a file from the remote,
    * and notify the transfer has been completed (but failed). We also call
    * AfterOppDisconnected here to ensure all variables will be cleaned.
    */
@@ -1487,86 +1456,56 @@ BluetoothOppManager::OnSocketDisconnect(
     FileTransferComplete();
     if (!mIsServer) {
       // Inform gaia of remaining blobs' sending failure
       DiscardBlobsToSend();
     }
   }
 
   AfterOppDisconnected();
-  mConnectedDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
+  mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
   mSuccessFlag = false;
 
   mSocket = nullptr;
   // Listen as a server if there's no more batch to process
   if (!ProcessNextBatch()) {
     Listen();
   }
 }
 
-void
-BluetoothOppManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
-                                         const nsAString& aServiceUuid,
-                                         int aChannel)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!aDeviceAddress.IsEmpty());
-
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-
-  if (aChannel < 0) {
-    if (mNeedsUpdatingSdpRecords) {
-      mNeedsUpdatingSdpRecords = false;
-      bs->UpdateSdpRecords(aDeviceAddress, this);
-    } else {
-      OnSocketConnectError(mSocket);
-    }
-
-    return;
-  }
-
-  if (!mSocket->Connect(NS_ConvertUTF16toUTF8(aDeviceAddress), aChannel)) {
-    OnSocketConnectError(mSocket);
-  }
-}
-
-void
-BluetoothOppManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!aDeviceAddress.IsEmpty());
-
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-
-  nsString uuid;
-  BluetoothUuidHelper::GetString(BluetoothServiceClass::OBJECT_PUSH, uuid);
-
-  if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, uuid, this))) {
-    OnSocketConnectError(mSocket);
-  }
-}
-
 NS_IMPL_ISUPPORTS1(BluetoothOppManager, nsIObserver)
 
 bool
 BluetoothOppManager::AcquireSdcardMountLock()
 {
   nsCOMPtr<nsIVolumeService> volumeSrv =
     do_GetService(NS_VOLUMESERVICE_CONTRACTID);
   NS_ENSURE_TRUE(volumeSrv, false);
   nsresult rv;
   rv = volumeSrv->CreateMountLock(NS_LITERAL_STRING("sdcard"),
                                   getter_AddRefs(mMountLock));
   NS_ENSURE_SUCCESS(rv, false);
   return true;
 }
 
 void
+BluetoothOppManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
+                                         const nsAString& aServiceUuid,
+                                         int aChannel)
+{
+  MOZ_ASSERT(false);
+}
+
+void
+BluetoothOppManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
+{
+  MOZ_ASSERT(false);
+}
+
+void
 BluetoothOppManager::Connect(const nsAString& aDeviceAddress,
                              BluetoothProfileController* aController)
 {
   MOZ_ASSERT(false);
 }
 
 void
 BluetoothOppManager::Disconnect(BluetoothProfileController* aController)
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.h
@@ -117,17 +117,17 @@ private:
    */
   bool ComposePacket(uint8_t aOpCode, UnixSocketRawData* aMessage);
 
   /**
    * OBEX session status.
    * Set when OBEX session is established.
    */
   bool mConnected;
-  nsString mConnectedDeviceAddress;
+  nsString mDeviceAddress;
 
   /**
    * Remote information
    */
   uint8_t mRemoteObexVersion;
   uint8_t mRemoteConnectionFlags;
   int mRemoteMaxPacketLength;
 
@@ -207,22 +207,20 @@ private:
   nsCOMPtr<nsIThread> mReadFileThread;
   nsCOMPtr<nsIOutputStream> mOutputStream;
   nsCOMPtr<nsIInputStream> mInputStream;
   nsCOMPtr<nsIVolumeMountLock> mMountLock;
   nsRefPtr<DeviceStorageFile> mDsFile;
 
   // If a connection has been established, mSocket will be the socket
   // communicating with the remote socket. We maintain the invariant that if
-  // mSocket is non-null, mRfcommSocket and mL2capSocket must be null (and vice
-  // versa).
+  // mSocket is non-null, mServerSocket must be null (and vice versa).
   nsRefPtr<BluetoothSocket> mSocket;
 
   // Server sockets. Once an inbound connection is established, it will hand
   // over the ownership to mSocket, and get a new server socket while Listen()
   // is called.
-  nsRefPtr<BluetoothSocket> mRfcommSocket;
-  nsRefPtr<BluetoothSocket> mL2capSocket;
+  nsRefPtr<BluetoothSocket> mServerSocket;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.cpp
@@ -1,77 +1,713 @@
 /* -*- 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 "BluetoothSocket.h"
 
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sock.h>
+#include <sys/socket.h>
+
+#include "base/message_loop.h"
+#include "BluetoothServiceBluedroid.h"
 #include "BluetoothSocketObserver.h"
-#include "BluetoothUnixSocketConnector.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/RefPtr.h"
 #include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
+#define FIRST_SOCKET_INFO_MSG_LENGTH 4
+#define TOTAL_SOCKET_INFO_LENGTH 20
 
 using namespace mozilla::ipc;
 USING_BLUETOOTH_NAMESPACE
 
+static const size_t MAX_READ_SIZE = 1 << 16;
+static const uint8_t UUID_OBEX_OBJECT_PUSH[] = {
+  0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+  0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+};
+static const btsock_interface_t* sBluetoothSocketInterface = nullptr;
+
+// helper functions
+static bool
+EnsureBluetoothSocketHalLoad()
+{
+  if (sBluetoothSocketInterface) {
+    return true;
+  }
+
+  const bt_interface_t* btInf = GetBluetoothInterface();
+  NS_ENSURE_TRUE(btInf, false);
+
+  sBluetoothSocketInterface =
+    (btsock_interface_t *) btInf->get_profile_interface(BT_PROFILE_SOCKETS_ID);
+  NS_ENSURE_TRUE(sBluetoothSocketInterface, false);
+
+  return true;
+}
+
+static int16_t
+ReadInt16(const uint8_t* aData, size_t* aOffset)
+{
+  int16_t value = (aData[*aOffset + 1] << 8) | aData[*aOffset];
+
+  *aOffset += 2;
+  return value;
+}
+
+static int32_t
+ReadInt32(const uint8_t* aData, size_t* aOffset)
+{
+  int32_t value = (aData[*aOffset + 3] << 24) |
+                  (aData[*aOffset + 2] << 16) |
+                  (aData[*aOffset + 1] << 8) |
+                  aData[*aOffset];
+  *aOffset += 4;
+  return value;
+}
+
+static void
+ReadBdAddress(const uint8_t* aData, size_t* aOffset, nsAString& aDeviceAddress)
+{
+  char bdstr[18];
+  sprintf(bdstr, "%02x:%02x:%02x:%02x:%02x:%02x",
+          aData[*aOffset], aData[*aOffset + 1], aData[*aOffset + 2],
+          aData[*aOffset + 3], aData[*aOffset + 4], aData[*aOffset + 5]);
+
+  aDeviceAddress.AssignLiteral(bdstr);
+  *aOffset += 6;
+}
+
+class mozilla::dom::bluetooth::DroidSocketImpl
+    : public MessageLoopForIO::Watcher
+{
+public:
+  DroidSocketImpl(BluetoothSocket* aConsumer, int aFd)
+    : mConsumer(aConsumer)
+    , mIOLoop(nullptr)
+    , mFd(aFd)
+    , mShuttingDownOnIOThread(false)
+  {
+  }
+
+  ~DroidSocketImpl()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  void QueueWriteData(UnixSocketRawData* aData)
+  {
+    mOutgoingQ.AppendElement(aData);
+    OnFileCanWriteWithoutBlocking(mFd);
+  }
+
+  bool IsShutdownOnMainThread()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mConsumer == nullptr;
+  }
+
+  bool IsShutdownOnIOThread()
+  {
+    return mShuttingDownOnIOThread;
+  }
+
+  void ShutdownOnMainThread()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(!IsShutdownOnMainThread());
+    mConsumer = nullptr;
+  }
+
+  void ShutdownOnIOThread()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(!mShuttingDownOnIOThread);
+
+    mReadWatcher.StopWatchingFileDescriptor();
+    mWriteWatcher.StopWatchingFileDescriptor();
+
+    mShuttingDownOnIOThread = true;
+  }
+
+  void SetUpIO(bool aWrite)
+  {
+    MOZ_ASSERT(!mIOLoop);
+    MOZ_ASSERT(mFd >= 0);
+    mIOLoop = MessageLoopForIO::current();
+
+    // Set up a read watch
+    mIOLoop->WatchFileDescriptor(mFd,
+                                 true,
+                                 MessageLoopForIO::WATCH_READ,
+                                 &mReadWatcher,
+                                 this);
+
+    if (aWrite) {
+      // Set up a write watch
+      mIOLoop->WatchFileDescriptor(mFd.get(),
+                                   false,
+                                   MessageLoopForIO::WATCH_WRITE,
+                                   &mWriteWatcher,
+                                   this);
+    }
+  }
+
+  void ConnectClientFd()
+  {
+    // Stop current read watch
+    mReadWatcher.StopWatchingFileDescriptor();
+    mIOLoop = nullptr;
+
+    // Restart read & write watch on client fd
+    SetUpIO(true);
+  }
+
+  /**
+   * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
+   * directly from main thread. All non-main-thread accesses should happen with
+   * mImpl as container.
+   */
+  RefPtr<BluetoothSocket> mConsumer;
+
+private:
+  /**
+   * libevent triggered functions that reads data from socket when available and
+   * guarenteed non-blocking. Only to be called on IO thread.
+   *
+   * @param aFd [in] File descriptor to read from
+   */
+  virtual void OnFileCanReadWithoutBlocking(int aFd);
+
+  /**
+   * libevent or developer triggered functions that writes data to socket when
+   * available and guarenteed non-blocking. Only to be called on IO thread.
+   *
+   * @param aFd [in] File descriptor to read from
+   */
+  virtual void OnFileCanWriteWithoutBlocking(int aFd);
+
+  /**
+   * Read message to get data and client fd wrapped in message header
+   *
+   * @param aFd     [in]  File descriptor to read message from
+   * @param aBuffer [out] Data buffer read
+   * @param aLength [out] Number of bytes read
+   */
+  ssize_t ReadMsg(int aFd, void *aBuffer, size_t aLength);
+
+  /**
+   * IO Loop pointer. Must be initalized and called from IO thread only.
+   */
+  MessageLoopForIO* mIOLoop;
+
+  /**
+   * Raw data queue. Must be pushed/popped from IO thread only.
+   */
+  typedef nsTArray<UnixSocketRawData* > UnixSocketRawDataQueue;
+  UnixSocketRawDataQueue mOutgoingQ;
+
+  /**
+   * Read watcher for libevent. Only to be accessed on IO Thread.
+   */
+  MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
+
+  /**
+   * Write watcher for libevent. Only to be accessed on IO Thread.
+   */
+  MessageLoopForIO::FileDescriptorWatcher mWriteWatcher;
+
+  /**
+   * File descriptor to read from/write to. Connection happens on user provided
+   * thread. Read/write/close happens on IO thread.
+   */
+  mozilla::ScopedClose mFd;
+
+  /**
+   * If true, do not requeue whatever task we're running
+   */
+  bool mShuttingDownOnIOThread;
+};
+
+template<class T>
+class DeleteInstanceRunnable : public nsRunnable
+{
+public:
+  DeleteInstanceRunnable(T* aInstance)
+  : mInstance(aInstance)
+  { }
+
+  NS_IMETHOD Run()
+  {
+    delete mInstance;
+
+    return NS_OK;
+  }
+
+private:
+  T* mInstance;
+};
+
+class RequestClosingSocketTask : public nsRunnable
+{
+public:
+  RequestClosingSocketTask(DroidSocketImpl* aImpl) : mImpl(aImpl)
+  {
+    MOZ_ASSERT(aImpl);
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mImpl->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;
+    }
+
+    // Start from here, same handling flow as calling CloseSocket() from
+    // upper layer
+    mImpl->mConsumer->CloseDroidSocket();
+    return NS_OK;
+  }
+private:
+  DroidSocketImpl* mImpl;
+};
+
+class ShutdownSocketTask : public Task {
+  virtual void Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    // At this point, there should be no new events on the IO thread after this
+    // one with the possible exception of a SocketAcceptTask that
+    // ShutdownOnIOThread will cancel for us. We are now fully shut down, so we
+    // can send a message to the main thread that will delete mImpl safely knowing
+    // that no more tasks reference it.
+    mImpl->ShutdownOnIOThread();
+
+    nsRefPtr<nsIRunnable> t(new DeleteInstanceRunnable<
+                                  mozilla::dom::bluetooth::DroidSocketImpl>(mImpl));
+    nsresult rv = NS_DispatchToMainThread(t);
+    NS_ENSURE_SUCCESS_VOID(rv);
+  }
+
+  DroidSocketImpl* mImpl;
+
+public:
+  ShutdownSocketTask(DroidSocketImpl* aImpl) : mImpl(aImpl) { }
+};
+
+class SocketReceiveTask : public nsRunnable
+{
+public:
+  SocketReceiveTask(DroidSocketImpl* aImpl, UnixSocketRawData* aData) :
+    mImpl(aImpl),
+    mRawData(aData)
+  {
+    MOZ_ASSERT(aImpl);
+    MOZ_ASSERT(aData);
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    if (mImpl->IsShutdownOnMainThread()) {
+      NS_WARNING("mConsumer is null, aborting receive!");
+      // 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;
+    }
+
+    MOZ_ASSERT(mImpl->mConsumer);
+    mImpl->mConsumer->ReceiveSocketData(mRawData);
+    return NS_OK;
+  }
+private:
+  DroidSocketImpl* mImpl;
+  nsAutoPtr<UnixSocketRawData> mRawData;
+};
+
+class SocketSendTask : public Task
+{
+public:
+  SocketSendTask(BluetoothSocket* aConsumer, DroidSocketImpl* aImpl,
+                 UnixSocketRawData* aData)
+    : mConsumer(aConsumer),
+      mImpl(aImpl),
+      mData(aData)
+  {
+    MOZ_ASSERT(aConsumer);
+    MOZ_ASSERT(aImpl);
+    MOZ_ASSERT(aData);
+  }
+
+  void
+  Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(!mImpl->IsShutdownOnIOThread());
+
+    mImpl->QueueWriteData(mData);
+  }
+
+private:
+  nsRefPtr<BluetoothSocket> mConsumer;
+  DroidSocketImpl* mImpl;
+  UnixSocketRawData* mData;
+};
+
+class SocketSetUpIOTask : public Task
+{
+  virtual void Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    mImpl->SetUpIO(mWrite);
+  }
+
+  DroidSocketImpl* mImpl;
+  bool mWrite;
+public:
+  SocketSetUpIOTask(DroidSocketImpl* aImpl, bool aWrite)
+  : mImpl(aImpl), mWrite(aWrite) { }
+};
+
+class SocketConnectClientFdTask : public Task
+{
+  virtual void Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    mImpl->ConnectClientFd();
+  }
+
+  DroidSocketImpl* mImpl;
+public:
+  SocketConnectClientFdTask(DroidSocketImpl* aImpl) : mImpl(aImpl) { }
+};
+
+ssize_t
+DroidSocketImpl::ReadMsg(int aFd, void *aBuffer, size_t aLength)
+{
+  ssize_t ret;
+  struct msghdr msg;
+  struct iovec iv;
+  struct cmsghdr cmsgbuf[2 * sizeof(cmsghdr) + 0x100];
+
+  memset(&msg, 0, sizeof(msg));
+  memset(&iv, 0, sizeof(iv));
+
+  iv.iov_base = (unsigned char *)aBuffer;
+  iv.iov_len = aLength;
+
+  msg.msg_iov = &iv;
+  msg.msg_iovlen = 1;
+  msg.msg_control = cmsgbuf;
+  msg.msg_controllen = sizeof(cmsgbuf);
+
+  ret = recvmsg(mFd.get(), &msg, MSG_NOSIGNAL);
+  if (ret < 0 && errno == EPIPE) {
+    // Treat this as an end of stream
+    return 0;
+  }
+
+  NS_ENSURE_FALSE(ret < 0, -1);
+  NS_ENSURE_FALSE(msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE), -1);
+
+  // Extract client fd from message header
+  for (struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg);
+       cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
+    if (cmsgptr->cmsg_level != SOL_SOCKET) {
+      continue;
+    }
+    if (cmsgptr->cmsg_type == SCM_RIGHTS) {
+      int *pDescriptors = (int *)CMSG_DATA(cmsgptr);
+      // Overwrite fd with client fd
+      mFd.reset(pDescriptors[0]);
+      break;
+    }
+  }
+
+  return ret;
+}
+
+void
+DroidSocketImpl::OnFileCanReadWithoutBlocking(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));
+
+    ssize_t ret;
+    if (!mConsumer->IsWaitingForClientFd()) {
+      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
+        }
+
+        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.
+      mReadWatcher.StopWatchingFileDescriptor();
+      mWriteWatcher.StopWatchingFileDescriptor();
+      nsRefPtr<RequestClosingSocketTask> t = new RequestClosingSocketTask(this);
+      NS_DispatchToMainThread(t);
+      return;
+    }
+
+    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;
+    }
+  }
+
+  MOZ_CRASH("We returned early");
+}
+
+void
+DroidSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!mShuttingDownOnIOThread);
+  MOZ_ASSERT(aFd >= 0);
+
+  // Try to write the bytes of mCurrentRilRawData.  If all were written, continue.
+  //
+  // Otherwise, save the byte position of the next byte to write
+  // within mCurrentWriteOffset, and request another write when the
+  // system won't block.
+  //
+  while (true) {
+    UnixSocketRawData* data;
+    if (mOutgoingQ.IsEmpty()) {
+      return;
+    }
+    data = mOutgoingQ.ElementAt(0);
+    const uint8_t *toWrite;
+    toWrite = data->mData;
+
+    while (data->mCurrentWriteOffset < data->mSize) {
+      ssize_t write_amount = data->mSize - data->mCurrentWriteOffset;
+      ssize_t written;
+      written = write (aFd, toWrite + data->mCurrentWriteOffset,
+                       write_amount);
+      if (written > 0) {
+        data->mCurrentWriteOffset += written;
+      }
+      if (written != write_amount) {
+        break;
+      }
+    }
+
+    if (data->mCurrentWriteOffset != data->mSize) {
+      MessageLoopForIO::current()->WatchFileDescriptor(
+        aFd,
+        false,
+        MessageLoopForIO::WATCH_WRITE,
+        &mWriteWatcher,
+        this);
+      return;
+    }
+    mOutgoingQ.RemoveElementAt(0);
+    delete data;
+  }
+}
+
 BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
                                  BluetoothSocketType aType,
                                  bool aAuth,
                                  bool aEncrypt)
   : mObserver(aObserver)
-  , mType(aType)
+  , mImpl(nullptr)
   , mAuth(aAuth)
   , mEncrypt(aEncrypt)
+  , mReceivedSocketInfoLength(0)
 {
   MOZ_ASSERT(aObserver);
+
+  EnsureBluetoothSocketHalLoad();
+  mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
+}
+
+void
+BluetoothSocket::CloseDroidSocket()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mImpl) {
+    return;
+  }
+
+  // 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 ShutdownSocketTask(mImpl));
+  mImpl = nullptr;
+
+  OnDisconnect();
 }
 
 bool
-BluetoothSocket::Connect(const nsACString& aDeviceAddress, int aChannel)
+BluetoothSocket::CreateDroidSocket(int aFd)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_FALSE(mImpl, false);
+
+  mImpl = new DroidSocketImpl(this, aFd);
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new SocketSetUpIOTask(mImpl, !mIsServer));
+
+  return true;
+}
+
+bool
+BluetoothSocket::Connect(const nsAString& aDeviceAddress, int aChannel)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!aDeviceAddress.IsEmpty());
+  NS_ENSURE_TRUE(sBluetoothSocketInterface, false);
 
-  nsAutoPtr<BluetoothUnixSocketConnector> c(
-    new BluetoothUnixSocketConnector(mType, aChannel, mAuth, mEncrypt));
+  bt_bdaddr_t remoteBdAddress;
+  StringToBdAddressType(aDeviceAddress, &remoteBdAddress);
 
-  if (!ConnectSocket(c.forget(), aDeviceAddress.BeginReading())) {
-    nsAutoString addr;
-    GetAddress(addr);
-    BT_LOGD("%s failed. Current connected device address: %s",
-           __FUNCTION__, NS_ConvertUTF16toUTF8(addr).get());
-    return false;
-  }
+  // TODO: uuid as argument
+  int fd;
+  NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
+    sBluetoothSocketInterface->connect((bt_bdaddr_t *) &remoteBdAddress,
+                                       (btsock_type_t) BTSOCK_RFCOMM,
+                                       UUID_OBEX_OBJECT_PUSH,
+                                       aChannel, &fd, (mAuth << 1) | mEncrypt),
+    false);
+  NS_ENSURE_TRUE(fd >= 0, false);
 
-  return true;
+  mIsServer = false;
+  return CreateDroidSocket(fd);
 }
 
 bool
 BluetoothSocket::Listen(int aChannel)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_TRUE(sBluetoothSocketInterface, false);
 
-  nsAutoPtr<BluetoothUnixSocketConnector> c(
-    new BluetoothUnixSocketConnector(mType, aChannel, mAuth, mEncrypt));
+  // TODO: uuid and service name as arguments
+  nsAutoCString serviceName("OBEX Object Push");
+  int fd;
+  NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
+    sBluetoothSocketInterface->listen((btsock_type_t) BTSOCK_RFCOMM,
+                                      serviceName.get(),
+                                      UUID_OBEX_OBJECT_PUSH,
+                                      aChannel, &fd, (mAuth << 1) | mEncrypt),
+    false);
+  NS_ENSURE_TRUE(fd >= 0, false);
+
+  mIsServer = true;
+  return CreateDroidSocket(fd);
+}
+
+bool
+BluetoothSocket::SendDroidSocketData(UnixSocketRawData* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_TRUE(mImpl, false);
+
+  MOZ_ASSERT(!mImpl->IsShutdownOnMainThread());
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new SocketSendTask(this, mImpl, aData));
+  return true;
+}
+
+bool
+BluetoothSocket::IsWaitingForClientFd()
+{
+  return (mIsServer &&
+          mReceivedSocketInfoLength == FIRST_SOCKET_INFO_MSG_LENGTH);
+}
 
-  if (!ListenSocket(c.forget())) {
-    nsAutoString addr;
-    GetAddress(addr);
-    BT_LOGD("%s failed. Current connected device address: %s",
-           __FUNCTION__, NS_ConvertUTF16toUTF8(addr).get());
+bool
+BluetoothSocket::ReceiveSocketInfo(nsAutoPtr<UnixSocketRawData>& aMessage)
+{
+  /**
+   * 2 socket info messages (20 bytes) to receive at the beginning:
+   * - 1st message: [channel:4]
+   * - 2nd message: [size:2][bd address:6][channel:4][connection status:4]
+   */
+  if (mReceivedSocketInfoLength >= TOTAL_SOCKET_INFO_LENGTH) {
+    // We've got both socket info messages
     return false;
   }
+  mReceivedSocketInfoLength += aMessage->mSize;
+
+  size_t offset = 0;
+  if (mReceivedSocketInfoLength == FIRST_SOCKET_INFO_MSG_LENGTH) {
+    // 1st message: [channel:4]
+    int32_t channel = ReadInt32(aMessage->mData, &offset);
+
+    BT_LOGR("%s: channel %d", __FUNCTION__, channel);
+  } else if (mReceivedSocketInfoLength == TOTAL_SOCKET_INFO_LENGTH) {
+    // 2nd message: [size:2][bd address:6][channel:4][connection status:4]
+    int16_t size = ReadInt16(aMessage->mData, &offset);
+    ReadBdAddress(aMessage->mData, &offset, mDeviceAddress);
+    int32_t channel = ReadInt32(aMessage->mData, &offset);
+    int32_t connectionStatus = ReadInt32(aMessage->mData, &offset);
+
+    BT_LOGR("%s: size %d channel %d remote addr %s status %d", __FUNCTION__,
+      size, channel, NS_ConvertUTF16toUTF8(mDeviceAddress).get(), connectionStatus);
+
+    if (connectionStatus != 0) {
+      OnConnectError();
+      return true;
+    }
+
+    if (mIsServer) {
+      // Connect client fd on IO thread
+      XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                       new SocketConnectClientFdTask(mImpl));
+    }
+    OnConnectSuccess();
+  }
 
   return true;
 }
 
 void
 BluetoothSocket::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage)
 {
+  if (ReceiveSocketInfo(aMessage)) {
+    return;
+  }
+
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
   mObserver->ReceiveSocketData(this, aMessage);
 }
 
 void
 BluetoothSocket::OnConnectSuccess()
 {
--- a/dom/bluetooth/bluedroid/BluetoothSocket.h
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.h
@@ -8,45 +8,80 @@
 #define mozilla_dom_bluetooth_BluetoothSocket_h
 
 #include "BluetoothCommon.h"
 #include "mozilla/ipc/UnixSocket.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocketObserver;
+class DroidSocketImpl;
 
 class BluetoothSocket : public mozilla::ipc::UnixSocketConsumer
 {
 public:
   BluetoothSocket(BluetoothSocketObserver* aObserver,
                   BluetoothSocketType aType,
                   bool aAuth,
                   bool aEncrypt);
 
-  bool Connect(const nsACString& aDeviceAddress, int aChannel);
+  /**
+   * 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
+   *    receives the 1st message immediately.
+   * 2) When there's incoming connection, DroidSocketImpl receives
+   *    2nd message to get socket info and client fd.
+   * 3) DroidSocketImpl stops watching read of original fd and
+   *    starts to watch read/write of client fd.
+   * 4) Obex server session starts.
+   */
   bool Listen(int aChannel);
+
   inline void Disconnect()
   {
-    CloseSocket();
+    CloseDroidSocket();
   }
 
   virtual void OnConnectSuccess() MOZ_OVERRIDE;
   virtual void OnConnectError() MOZ_OVERRIDE;
   virtual void OnDisconnect() MOZ_OVERRIDE;
   virtual void ReceiveSocketData(
     nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage) MOZ_OVERRIDE;
 
   inline void GetAddress(nsAString& aDeviceAddress)
   {
-    GetSocketAddr(aDeviceAddress);
+    aDeviceAddress = mDeviceAddress;
   }
 
+  void CloseDroidSocket();
+  bool IsWaitingForClientFd();
+  bool SendDroidSocketData(mozilla::ipc::UnixSocketRawData* aData);
+
 private:
   BluetoothSocketObserver* mObserver;
-  BluetoothSocketType mType;
+  DroidSocketImpl* mImpl;
+  nsString mDeviceAddress;
   bool mAuth;
   bool mEncrypt;
+  bool mIsServer;
+  int mReceivedSocketInfoLength;
+
+  bool CreateDroidSocket(int aFd);
+  bool ReceiveSocketInfo(nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage);
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/bluedroid/gonk/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/gonk/BluetoothServiceBluedroid.cpp
@@ -15,18 +15,19 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 
 #include "BluetoothServiceBluedroid.h"
 
 #include <hardware/hardware.h>
 
-#include "bluedroid/BluetoothA2dpManager.h"
-#include "bluedroid/BluetoothHfpManager.h"
+#include "BluetoothA2dpManager.h"
+#include "BluetoothHfpManager.h"
+#include "BluetoothOppManager.h"
 #include "BluetoothProfileController.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothUtils.h"
 #include "BluetoothUuid.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
@@ -110,16 +111,22 @@ public:
     // Try to fire event 'AdapterAdded' to fit the original behaviour when
     // we used BlueZ as backend.
     BluetoothService* bs = BluetoothService::Get();
     NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
 
     bs->AdapterAddedReceived();
     bs->TryFiringAdapterAdded();
 
+    // Trigger BluetoothOppManager to listen
+    BluetoothOppManager* opp = BluetoothOppManager::Get();
+    if (!opp || !opp->Listen()) {
+      BT_LOGR("%s: Fail to start BluetoothOppManager listening", __FUNCTION__);
+    }
+
     return NS_OK;
   }
 };
 
 /**
  *  Static callback functions
  */
 static void
@@ -1285,32 +1292,75 @@ BluetoothServiceBluedroid::Disconnect(
 }
 
 void
 BluetoothServiceBluedroid::SendFile(const nsAString& aDeviceAddress,
                                     BlobParent* aBlobParent,
                                     BlobChild* aBlobChild,
                                     BluetoothReplyRunnable* aRunnable)
 {
+  MOZ_ASSERT(NS_IsMainThread());
 
+  // Force to stop discovery, otherwise socket connecting would fail
+  if (!IsReady() || BT_STATUS_SUCCESS != sBtInterface->cancel_discovery()) {
+    NS_NAMED_LITERAL_STRING(errorStr, "Calling cancel_discovery() failed");
+    DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr);
+    return;
+  }
+
+  // Currently we only support one device sending one file at a time,
+  // so we don't need aDeviceAddress here because the target device
+  // has been determined when calling 'Connect()'. Nevertheless, keep
+  // it for future use.
+  BluetoothOppManager* opp = BluetoothOppManager::Get();
+  nsAutoString errorStr;
+  if (!opp || !opp->SendFile(aDeviceAddress, aBlobParent)) {
+    errorStr.AssignLiteral("Calling SendFile() failed");
+  }
+
+  DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr);
 }
 
 void
 BluetoothServiceBluedroid::StopSendingFile(const nsAString& aDeviceAddress,
                                            BluetoothReplyRunnable* aRunnable)
 {
+  MOZ_ASSERT(NS_IsMainThread());
 
+  // Currently we only support one device sending one file at a time,
+  // so we don't need aDeviceAddress here because the target device
+  // has been determined when calling 'Connect()'. Nevertheless, keep
+  // it for future use.
+  BluetoothOppManager* opp = BluetoothOppManager::Get();
+  nsAutoString errorStr;
+  if (!opp || !opp->StopSendingFile()) {
+    errorStr.AssignLiteral("Calling StopSendingFile() failed");
+  }
+
+  DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr);
 }
 
 void
 BluetoothServiceBluedroid::ConfirmReceivingFile(
   const nsAString& aDeviceAddress, bool aConfirm,
   BluetoothReplyRunnable* aRunnable)
 {
+  MOZ_ASSERT(NS_IsMainThread(), "Must be called from main thread!");
 
+  // Currently we only support one device sending one file at a time,
+  // so we don't need aDeviceAddress here because the target device
+  // has been determined when calling 'Connect()'. Nevertheless, keep
+  // it for future use.
+  BluetoothOppManager* opp = BluetoothOppManager::Get();
+  nsAutoString errorStr;
+  if (!opp || !opp->ConfirmReceivingFile(aConfirm)) {
+    errorStr.AssignLiteral("Calling ConfirmReceivingFile() failed");
+  }
+
+  DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr);
 }
 
 void
 BluetoothServiceBluedroid::ConnectSco(BluetoothReplyRunnable* aRunnable)
 {
 
 }
 
--- a/dom/bluetooth/bluez/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/bluez/linux/BluetoothDBusService.cpp
@@ -2966,20 +2966,18 @@ BluetoothDBusService::SendFile(const nsA
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Currently we only support one device sending one file at a time,
   // so we don't need aDeviceAddress here because the target device
   // has been determined when calling 'Connect()'. Nevertheless, keep
   // it for future use.
   BluetoothOppManager* opp = BluetoothOppManager::Get();
-  NS_ENSURE_TRUE_VOID(opp);
-
   nsAutoString errorStr;
-  if (!opp->SendFile(aDeviceAddress, aBlobParent)) {
+  if (!opp || !opp->SendFile(aDeviceAddress, aBlobParent)) {
     errorStr.AssignLiteral("Calling SendFile() failed");
   }
 
   DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr);
 }
 
 void
 BluetoothDBusService::StopSendingFile(const nsAString& aDeviceAddress,
@@ -2987,42 +2985,38 @@ BluetoothDBusService::StopSendingFile(co
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Currently we only support one device sending one file at a time,
   // so we don't need aDeviceAddress here because the target device
   // has been determined when calling 'Connect()'. Nevertheless, keep
   // it for future use.
   BluetoothOppManager* opp = BluetoothOppManager::Get();
-  NS_ENSURE_TRUE_VOID(opp);
-
   nsAutoString errorStr;
-  if (!opp->StopSendingFile()) {
+  if (!opp || !opp->StopSendingFile()) {
     errorStr.AssignLiteral("Calling StopSendingFile() failed");
   }
 
   DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr);
 }
 
 void
 BluetoothDBusService::ConfirmReceivingFile(const nsAString& aDeviceAddress,
                                            bool aConfirm,
                                            BluetoothReplyRunnable* aRunnable)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
+  MOZ_ASSERT(NS_IsMainThread(), "Must be called from main thread!");
 
   // Currently we only support one device sending one file at a time,
   // so we don't need aDeviceAddress here because the target device
   // has been determined when calling 'Connect()'. Nevertheless, keep
   // it for future use.
   BluetoothOppManager* opp = BluetoothOppManager::Get();
-  NS_ENSURE_TRUE_VOID(opp);
-
   nsAutoString errorStr;
-  if (!opp->ConfirmReceivingFile(aConfirm)) {
+  if (!opp || !opp->ConfirmReceivingFile(aConfirm)) {
     errorStr.AssignLiteral("Calling ConfirmReceivingFile() failed");
   }
 
   DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr);
 }
 
 void
 BluetoothDBusService::ConnectSco(BluetoothReplyRunnable* aRunnable)