Bug 1036977: Remove socket setup from BluetoothSocket (under bluetooth2/), r=btian
authorThomas Zimmermann <tdz@users.sourceforge.net>
Wed, 16 Jul 2014 10:25:10 +0200
changeset 194439 8a61cf67dd415a1e1cd6d75c32c11aac2ba36170
parent 194438 60eabd556f3bae0a313e800ec10b8b9d4ea29180
child 194440 558f3c896e482e03c1bc058f2e1bf49befb39f91
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: Remove socket setup from BluetoothSocket (under bluetooth2/), r=btian This patch removes all code related to socket setup from Bluedroid's BluetoothSocket. The socket setup is handled by BluetoothInterface; transparantly to its users. Since most of the socket setup is now hidden, a comment was added to DroidSocketImpl that explains the connection phases in server and client.
dom/bluetooth2/bluedroid/BluetoothSocket.cpp
dom/bluetooth2/bluedroid/BluetoothSocket.h
--- a/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
@@ -13,19 +13,16 @@
 #include "BluetoothInterface.h"
 #include "BluetoothSocketObserver.h"
 #include "BluetoothUtils.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
 };
@@ -43,87 +40,72 @@ EnsureBluetoothSocketHalLoad()
   NS_ENSURE_TRUE(btInf, false);
 
   sBluetoothSocketInterface = btInf->GetBluetoothSocketInterface();
   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 ipc::UnixFdWatcher
 {
 public:
+  /* The connection status in DroidSocketImpl indicates the current
+   * phase of the socket connection. The initial settign should always
+   * be DISCONNECTED, when no connection is present.
+   *
+   * To establish a connection on the server, DroidSocketImpl moves
+   * to LISTENING. It now waits for incoming connection attempts by
+   * installing a read watcher on the I/O thread. When its socket file
+   * descriptor becomes readable, DroidSocketImpl accepts the connection
+   * and finally moves DroidSocketImpl to CONNECTED. DroidSocketImpl now
+   * uses read and write watchers during data transfers. Any socket setup
+   * is handled internally by the accept method.
+   *
+   * On the client side, DroidSocketImpl moves to CONNECTING and installs
+   * a write watcher on the I/O thread to wait until the connection is
+   * ready. The socket setup is handled internally by the connect method.
+   * Installing the write handler makes the code compatible with POSIX
+   * semantics for non-blocking connects and gives a clear signal when the
+   * conncetion is ready. DroidSocketImpl then moves to CONNECTED and uses
+   * read and write watchers during data transfers.
+   */
   enum ConnectionStatus {
     SOCKET_IS_DISCONNECTED = 0,
     SOCKET_IS_LISTENING,
     SOCKET_IS_CONNECTING,
     SOCKET_IS_CONNECTED
   };
 
   DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer, int aFd)
     : ipc::UnixFdWatcher(aIOLoop, aFd)
     , mConsumer(aConsumer)
-    , mReadMsgForClientFd(false)
     , mShuttingDownOnIOThread(false)
     , mChannel(0)
     , mAuth(false)
     , mEncrypt(false)
     , mConnectionStatus(SOCKET_IS_DISCONNECTED)
   { }
 
   DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer,
                   int aChannel, bool aAuth, bool aEncrypt)
     : ipc::UnixFdWatcher(aIOLoop)
     , mConsumer(aConsumer)
-    , mReadMsgForClientFd(false)
     , mShuttingDownOnIOThread(false)
     , mChannel(aChannel)
     , mAuth(aAuth)
     , mEncrypt(aEncrypt)
     , mConnectionStatus(SOCKET_IS_DISCONNECTED)
   { }
 
   DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer,
                   const nsAString& aDeviceAddress,
                   int aChannel, bool aAuth, bool aEncrypt)
     : ipc::UnixFdWatcher(aIOLoop)
     , mConsumer(aConsumer)
-    , mReadMsgForClientFd(false)
     , mShuttingDownOnIOThread(false)
     , mDeviceAddress(aDeviceAddress)
     , mChannel(aChannel)
     , mAuth(aAuth)
     , mEncrypt(aEncrypt)
     , mConnectionStatus(SOCKET_IS_DISCONNECTED)
   {
     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
@@ -186,21 +168,16 @@ public:
 
   /**
    * 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;
 
-  /**
-   * If true, read message header to get client fd.
-   */
-  bool mReadMsgForClientFd;
-
 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);
@@ -214,25 +191,16 @@ private:
   virtual void OnFileCanWriteWithoutBlocking(int aFd);
 
   void OnSocketCanReceiveWithoutBlocking(int aFd);
   void OnSocketCanAcceptWithoutBlocking(int aFd);
   void OnSocketCanSendWithoutBlocking(int aFd);
   void OnSocketCanConnectWithoutBlocking(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);
-
-  /**
    * Raw data queue. Must be pushed/popped from IO thread only.
    */
   typedef nsTArray<UnixSocketRawData* > UnixSocketRawDataQueue;
   UnixSocketRawDataQueue mOutgoingQ;
 
   /**
    * If true, do not requeue whatever task we're running
    */
@@ -575,70 +543,16 @@ DroidSocketImpl::Accept(int aFd)
   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];
-
-  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(GetFd(), &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 != nullptr; 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
-      int fd = pDescriptors[0];
-      int flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
-      NS_ENSURE_TRUE(flags >= 0, 0);
-      if (!(flags & O_NONBLOCK)) {
-        int res = TEMP_FAILURE_RETRY(fcntl(fd, F_SETFL, flags | O_NONBLOCK));
-        NS_ENSURE_TRUE(!res, 0);
-      }
-      Close();
-      SetFd(fd);
-      break;
-    }
-  }
-
-  return ret;
-}
-
 void
 DroidSocketImpl::OnFileCanReadWithoutBlocking(int aFd)
 {
   if (mConnectionStatus == SOCKET_IS_CONNECTED) {
     OnSocketCanReceiveWithoutBlocking(aFd);
   } else if (mConnectionStatus == SOCKET_IS_LISTENING) {
     OnSocketCanAcceptWithoutBlocking(aFd);
   } else {
@@ -867,17 +781,16 @@ DroidSocketImpl::OnSocketCanConnectWitho
 BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
                                  BluetoothSocketType aType,
                                  bool aAuth,
                                  bool aEncrypt)
   : mObserver(aObserver)
   , mImpl(nullptr)
   , mAuth(aAuth)
   , mEncrypt(aEncrypt)
-  , mReceivedSocketInfoLength(0)
 {
   MOZ_ASSERT(aObserver);
 
   EnsureBluetoothSocketHalLoad();
   mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
 }
 
 void
@@ -931,17 +844,16 @@ private:
 };
 
 bool
 BluetoothSocket::Connect(const nsAString& aDeviceAddress, int aChannel)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_FALSE(mImpl, false);
 
-  mIsServer = false;
   mImpl = new DroidSocketImpl(XRE_GetIOMessageLoop(), this, aDeviceAddress,
                               aChannel, mAuth, mEncrypt);
 
   bt_bdaddr_t remoteBdAddress;
   StringToBdAddressType(aDeviceAddress, &remoteBdAddress);
 
   // TODO: uuid as argument
   sBluetoothSocketInterface->Connect(&remoteBdAddress,
@@ -983,17 +895,16 @@ private:
 };
 
 bool
 BluetoothSocket::Listen(int aChannel)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_FALSE(mImpl, false);
 
-  mIsServer = true;
   mImpl = new DroidSocketImpl(XRE_GetIOMessageLoop(), this, aChannel, mAuth,
                               mEncrypt);
 
   sBluetoothSocketInterface->Listen(BTSOCK_RFCOMM,
                                     "OBEX Object Push",
                                     UUID_OBEX_OBJECT_PUSH,
                                     aChannel,
                                     (BTSOCK_FLAG_ENCRYPT * mEncrypt) |
@@ -1009,65 +920,16 @@ BluetoothSocket::SendDroidSocketData(Uni
   NS_ENSURE_TRUE(mImpl, false);
 
   MOZ_ASSERT(!mImpl->IsShutdownOnMainThread());
   XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
                                    new SocketSendTask(this, mImpl, aData));
   return true;
 }
 
-bool
-BluetoothSocket::ReceiveSocketInfo(nsAutoPtr<UnixSocketRawData>& aMessage)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  /**
-   * 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("channel %d", channel);
-
-    // If this is server socket, read header of next message for client fd
-    mImpl->mReadMsgForClientFd = mIsServer;
-  } 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("size %d channel %d remote addr %s status %d",
-      size, channel, NS_ConvertUTF16toUTF8(mDeviceAddress).get(), connectionStatus);
-
-    if (connectionStatus != 0) {
-      NotifyError();
-      return true;
-    }
-
-    mImpl->mReadMsgForClientFd = false;
-    // Connect client fd on IO thread
-    XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-                                     new SocketConnectClientFdTask(mImpl));
-    NotifySuccess();
-  }
-
-  return true;
-}
-
 void
 BluetoothSocket::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
   mObserver->ReceiveSocketData(this, aMessage);
 }
 
@@ -1089,9 +951,8 @@ BluetoothSocket::OnConnectError()
 
 void
 BluetoothSocket::OnDisconnect()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
   mObserver->OnSocketDisconnect(this);
 }
-
--- a/dom/bluetooth2/bluedroid/BluetoothSocket.h
+++ b/dom/bluetooth2/bluedroid/BluetoothSocket.h
@@ -52,17 +52,13 @@ public:
   bool SendDroidSocketData(mozilla::ipc::UnixSocketRawData* aData);
 
 private:
   BluetoothSocketObserver* mObserver;
   DroidSocketImpl* mImpl;
   nsString mDeviceAddress;
   bool mAuth;
   bool mEncrypt;
-  bool mIsServer;
-  int mReceivedSocketInfoLength;
-
-  bool ReceiveSocketInfo(nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage);
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif