Bug 1036977: Introduce connection status for Bluedroid sockets (under bluetooth2/), r=btian
authorThomas Zimmermann <tdz@users.sourceforge.net>
Wed, 16 Jul 2014 10:24:19 +0200
changeset 194234 b709235dd6b7a8a04edebfa0695c34e4ee86f789
parent 194233 1f9016933323f33b2d2216b651f1e4a9edac711e
child 194235 bea73261c79229a3fa07694529f52037bfa1b304
push id8941
push usertdz@users.sourceforge.net
push dateWed, 16 Jul 2014 08:27:50 +0000
treeherderb2g-inbound@8a61cf67dd41 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbtian
bugs1036977
milestone33.0a1
Bug 1036977: Introduce connection status for Bluedroid sockets (under bluetooth2/), r=btian In preparaton of moving the Bluedroid socket setup to BluetoothInterface, this patch introduces connection states for Bluetooth sockets. There are 4 states, - Disconnected, - Listening, - Connecting, and - Connected. All sockets start in Disconnected and transition to Connected via one of the other states. Server socket transition through Listening, Client sockets transition through Connecting. There is currently a lot of code duplication in read and write methods. This will be cleaned up when the connection setup is handled by BluetoothInterface.
dom/bluetooth2/bluedroid/BluetoothSocket.cpp
--- a/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
@@ -78,49 +78,58 @@ ReadBdAddress(const uint8_t* aData, size
 
   aDeviceAddress.AssignLiteral(bdstr);
   *aOffset += 6;
 }
 
 class mozilla::dom::bluetooth::DroidSocketImpl : public ipc::UnixFdWatcher
 {
 public:
+  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());
   }
 
   ~DroidSocketImpl()
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
@@ -157,31 +166,26 @@ public:
     RemoveWatchers(READ_WATCHER | WRITE_WATCHER);
 
     mShuttingDownOnIOThread = true;
   }
 
   void Connect(int aFd);
   void Listen(int aFd);
 
-  void SetUpIO(bool aWrite)
-  {
-    AddWatchers(READ_WATCHER, true);
-    if (aWrite) {
-        AddWatchers(WRITE_WATCHER, false);
-    }
-  }
-
   void ConnectClientFd()
   {
     // Stop current read watch
     RemoveWatchers(READ_WATCHER);
 
+    mConnectionStatus = SOCKET_IS_CONNECTED;
+
     // Restart read & write watch on client fd
-    SetUpIO(true);
+    AddWatchers(READ_WATCHER, true);
+    AddWatchers(WRITE_WATCHER, false);
   }
 
   /**
    * 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;
@@ -203,16 +207,21 @@ private:
   /**
    * 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);
 
+  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);
@@ -227,16 +236,17 @@ private:
    * If true, do not requeue whatever task we're running
    */
   bool mShuttingDownOnIOThread;
 
   nsString mDeviceAddress;
   int mChannel;
   bool mAuth;
   bool mEncrypt;
+  ConnectionStatus mConnectionStatus;
 };
 
 template<class T>
 class DeleteInstanceRunnable : public nsRunnable
 {
 public:
   DeleteInstanceRunnable(T* aInstance)
   : mInstance(aInstance)
@@ -450,16 +460,17 @@ DroidSocketImpl::Connect(int aFd)
   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_CONNECTING;
 
   AddWatchers(READ_WATCHER, true);
 }
 
 void
 DroidSocketImpl::Listen(int aFd)
 {
   MOZ_ASSERT(aFd >= 0);
@@ -468,16 +479,18 @@ DroidSocketImpl::Listen(int aFd)
   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_LISTENING;
+
   AddWatchers(READ_WATCHER, true);
 }
 
 ssize_t
 DroidSocketImpl::ReadMsg(int aFd, void *aBuffer, size_t aLength)
 {
   ssize_t ret;
   struct msghdr msg;
@@ -528,16 +541,79 @@ DroidSocketImpl::ReadMsg(int aFd, void *
   }
 
   return ret;
 }
 
 void
 DroidSocketImpl::OnFileCanReadWithoutBlocking(int aFd)
 {
+  if (mConnectionStatus == SOCKET_IS_CONNECTED) {
+    OnSocketCanReceiveWithoutBlocking(aFd);
+  } else if (mConnectionStatus == SOCKET_IS_LISTENING) {
+    OnSocketCanAcceptWithoutBlocking(aFd);
+  } else if (mConnectionStatus == SOCKET_IS_CONNECTING) {
+    /* TODO: remove this branch when connect is handled by BluetoothInterface */
+    OnSocketCanConnectWithoutBlocking(aFd);
+  } else {
+    NS_NOTREACHED("invalid connection state for reading");
+  }
+}
+
+void
+DroidSocketImpl::OnSocketCanReceiveWithoutBlocking(int aFd)
+{
+  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 = read(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.
+      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");
+}
+
+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;
@@ -581,16 +657,28 @@ DroidSocketImpl::OnFileCanReadWithoutBlo
   }
 
   MOZ_CRASH("We returned early");
 }
 
 void
 DroidSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
 {
+  if (mConnectionStatus == SOCKET_IS_CONNECTED) {
+    OnSocketCanSendWithoutBlocking(aFd);
+  } else if (mConnectionStatus == SOCKET_IS_CONNECTING) {
+    OnSocketCanConnectWithoutBlocking(aFd);
+  } else {
+    NS_NOTREACHED("invalid connection state for writing");
+  }
+}
+
+void
+DroidSocketImpl::OnSocketCanSendWithoutBlocking(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
@@ -621,16 +709,69 @@ DroidSocketImpl::OnFileCanWriteWithoutBl
     if (data->mCurrentWriteOffset != data->mSize) {
       AddWatchers(WRITE_WATCHER, false);
     }
     mOutgoingQ.RemoveElementAt(0);
     delete data;
   }
 }
 
+void
+DroidSocketImpl::OnSocketCanConnectWithoutBlocking(int aFd)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!mShuttingDownOnIOThread);
+
+  // Read all of the incoming data.
+  while (true) {
+    nsAutoPtr<UnixSocketRawData> incoming(new UnixSocketRawData(MAX_READ_SIZE));
+
+    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
+        }
+
+        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");
+}
+
 BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
                                  BluetoothSocketType aType,
                                  bool aAuth,
                                  bool aEncrypt)
   : mObserver(aObserver)
   , mImpl(nullptr)
   , mAuth(aAuth)
   , mEncrypt(aEncrypt)
@@ -653,17 +794,17 @@ BluetoothSocket::CloseDroidSocket()
   // 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();
+  NotifyDisconnect();
 }
 
 class ConnectResultHandler MOZ_FINAL : public BluetoothSocketResultHandler
 {
 public:
   ConnectResultHandler(DroidSocketImpl* aImpl)
   : mImpl(aImpl)
   {
@@ -801,27 +942,25 @@ BluetoothSocket::ReceiveSocketInfo(nsAut
     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) {
-      OnConnectError();
+      NotifyError();
       return true;
     }
 
-    if (mIsServer) {
-      mImpl->mReadMsgForClientFd = false;
-      // Connect client fd on IO thread
-      XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-                                       new SocketConnectClientFdTask(mImpl));
-    }
-    OnConnectSuccess();
+    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)
 {