Bug 1029387: Handle socket setup for |Accept| in BluetoothInterface, r=shuang
authorThomas Zimmermann <tdz@users.sourceforge.net>
Thu, 10 Jul 2014 15:10:54 +0200
changeset 214126 70396960e0740e94e6f5e329cd78c4265bc356a4
parent 214125 9834dab5eef5180b8c1a66ddba46ea76b222b8d4
child 214127 ef86887e089060fe8cc9043bafcae8b84e1d9d3e
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshuang
bugs1029387
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 1029387: Handle socket setup for |Accept| in BluetoothInterface, r=shuang This patch moves the accept phase of Bluedroid's |Listen| to the implementation of BluetoothInterface. |BluetoothInterface::Accept| handles Bluedroid's socket-setup messages and executes the result handler with the received file descriptor and data.
dom/bluetooth/bluedroid/BluetoothInterface.cpp
dom/bluetooth/bluedroid/BluetoothInterface.h
dom/bluetooth/bluedroid/BluetoothSocket.cpp
dom/bluetooth/bluedroid/BluetoothSocket.h
--- a/dom/bluetooth/bluedroid/BluetoothInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothInterface.cpp
@@ -501,16 +501,59 @@ BluetoothSocketInterface::Connect(const 
     XRE_GetIOMessageLoop()->PostTask(FROM_HERE, t);
   } else if (aRes) {
       DispatchBluetoothSocketResult(aRes,
                                     &BluetoothSocketResultHandler::Connect,
                                     -1, EmptyString(), 0, status);
   }
 }
 
+/* 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);
+  }
+
+  void Proceed(bt_status_t aStatus) MOZ_OVERRIDE
+  {
+    if (mRes) {
+      DispatchBluetoothSocketResult(mRes,
+                                    &BluetoothSocketResultHandler::Accept,
+                                    GetClientFd(), GetBdAddress(),
+                                    GetConnectionStatus(),
+                                    aStatus);
+    }
+    MessageLoopForIO::current()->PostTask(
+      FROM_HERE, new DeleteTask<AcceptWatcher>(this));
+  }
+
+private:
+  nsRefPtr<BluetoothSocketResultHandler> mRes;
+};
+
+void
+BluetoothSocketInterface::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);
+}
+
 BluetoothSocketInterface::BluetoothSocketInterface(
   const btsock_interface_t* aInterface)
 : mInterface(aInterface)
 {
   MOZ_ASSERT(mInterface);
 }
 
 BluetoothSocketInterface::~BluetoothSocketInterface()
--- a/dom/bluetooth/bluedroid/BluetoothInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothInterface.h
@@ -34,16 +34,18 @@ public:
   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, const nsAString& aBdAddress,
                        int aConnectionState) { }
+  virtual void Accept(int aSockFd, const nsAString& aBdAddress,
+                      int aConnectionState) { }
 };
 
 class BluetoothSocketInterface
 {
 public:
   friend class BluetoothInterface;
 
   // Init and Cleanup is handled by BluetoothInterface
@@ -51,16 +53,18 @@ public:
   void Listen(btsock_type_t aType,
               const char* aServiceName, const uint8_t* aServiceUuid,
               int aChannel, int aFlags, BluetoothSocketResultHandler* aRes);
 
   void Connect(const bt_bdaddr_t* aBdAddr, btsock_type_t aType,
                const uint8_t* aUuid, int aChannel, int aFlags,
                BluetoothSocketResultHandler* aRes);
 
+  void Accept(int aFd, BluetoothSocketResultHandler* aRes);
+
 protected:
   BluetoothSocketInterface(const btsock_interface_t* aInterface);
   ~BluetoothSocketInterface();
 
 private:
   const btsock_interface_t* mInterface;
 };
 
--- a/dom/bluetooth/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.cpp
@@ -165,16 +165,17 @@ public:
 
     RemoveWatchers(READ_WATCHER | WRITE_WATCHER);
 
     mShuttingDownOnIOThread = true;
   }
 
   void Connect(int aFd);
   void Listen(int aFd);
+  void Accept(int aFd);
 
   void ConnectClientFd()
   {
     // Stop current read watch
     RemoveWatchers(READ_WATCHER);
 
     mConnectionStatus = SOCKET_IS_CONNECTED;
 
@@ -548,16 +549,42 @@ DroidSocketImpl::Listen(int aFd)
   }
 
   SetFd(aFd);
   mConnectionStatus = SOCKET_IS_LISTENING;
 
   AddWatchers(READ_WATCHER, true);
 }
 
+void
+DroidSocketImpl::Accept(int aFd)
+{
+  Close();
+
+  int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
+  NS_ENSURE_TRUE_VOID(flags >= 0);
+
+  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_CONNECTED;
+
+  nsRefPtr<OnSocketEventRunnable> r =
+    new OnSocketEventRunnable(this, OnSocketEventRunnable::CONNECT_SUCCESS);
+  NS_DispatchToMainThread(r);
+
+  AddWatchers(READ_WATCHER, true);
+  if (!mOutgoingQ.IsEmpty()) {
+    AddWatchers(WRITE_WATCHER, false);
+  }
+}
+
 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];
 
@@ -662,67 +689,107 @@ DroidSocketImpl::OnSocketCanReceiveWitho
     if (ret < ssize_t(MAX_READ_SIZE)) {
       return;
     }
   }
 
   MOZ_CRASH("We returned early");
 }
 
+class AcceptTask MOZ_FINAL : public DroidSocketImplTask
+{
+public:
+  AcceptTask(DroidSocketImpl* aDroidSocketImpl, int aFd)
+  : DroidSocketImplTask(aDroidSocketImpl)
+  , mFd(aFd)
+  { }
+
+  void Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(!IsCanceled());
+
+    GetDroidSocketImpl()->Accept(mFd);
+  }
+
+private:
+  int mFd;
+};
+
+class AcceptResultHandler MOZ_FINAL : public BluetoothSocketResultHandler
+{
+public:
+  AcceptResultHandler(DroidSocketImpl* aImpl)
+  : mImpl(aImpl)
+  {
+    MOZ_ASSERT(mImpl);
+  }
+
+  void Accept(int aFd, const nsAString& aBdAddress,
+              int aConnectionStatus) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mImpl->IsShutdownOnMainThread()) {
+      BT_LOGD("mConsumer is null, aborting receive!");
+      return;
+    }
+
+    mImpl->mConsumer->SetAddress(aBdAddress);
+    XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new AcceptTask(mImpl, aFd));
+  }
+
+  void OnError(bt_status_t aStatus) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    BT_LOGR("BluetoothSocketInterface::Accept failed: %d", (int)aStatus);
+  }
+
+private:
+  DroidSocketImpl* mImpl;
+};
+
+class AcceptRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  AcceptRunnable(int aFd, DroidSocketImpl* aImpl)
+  : mFd(aFd)
+  , mImpl(aImpl)
+  {
+    MOZ_ASSERT(mImpl);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(sBluetoothSocketInterface);
+
+    sBluetoothSocketInterface->Accept(mFd, new AcceptResultHandler(mImpl));
+
+    return NS_OK;
+  }
+
+private:
+  int mFd;
+  DroidSocketImpl* mImpl;
+};
+
 void
 DroidSocketImpl::OnSocketCanAcceptWithoutBlocking(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 (!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
-        }
+  /* When a listening socket is ready for receiving data,
+   * we can call |Accept| on it.
+   */
 
-        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;
-    }
-
-    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");
+  RemoveWatchers(READ_WATCHER);
+  nsRefPtr<AcceptRunnable> t = new AcceptRunnable(aFd, this);
+  NS_DispatchToMainThread(t);
 }
 
 void
 DroidSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
 {
   if (mConnectionStatus == SOCKET_IS_CONNECTED) {
     OnSocketCanSendWithoutBlocking(aFd);
   } else if (mConnectionStatus == SOCKET_IS_CONNECTING) {
@@ -994,21 +1061,16 @@ BluetoothSocket::ReceiveSocketInfo(nsAut
   }
 
   return true;
 }
 
 void
 BluetoothSocket::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage)
 {
-  /* clients handle socket setup in BluetoothInterface::Connect */
-  if (mIsServer && 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
@@ -20,29 +20,16 @@ class BluetoothSocket : public mozilla::
 public:
   BluetoothSocket(BluetoothSocketObserver* aObserver,
                   BluetoothSocketType aType,
                   bool aAuth,
                   bool aEncrypt);
 
   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()
   {
     CloseDroidSocket();
   }
 
   virtual void OnConnectSuccess() MOZ_OVERRIDE;