Bug 1029387: Remove socket setup from BluetoothSocket, r=shuang

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.
This commit is contained in:
Thomas Zimmermann 2014-07-10 15:11:09 +02:00
parent 8751f4be58
commit c8a8d0d59c
2 changed files with 20 additions and 163 deletions

View File

@ -18,9 +18,6 @@
#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
@ -48,41 +45,29 @@ EnsureBluetoothSocketHalLoad()
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,
@ -93,7 +78,6 @@ public:
DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer, int aFd)
: ipc::UnixFdWatcher(aIOLoop, aFd)
, mConsumer(aConsumer)
, mReadMsgForClientFd(false)
, mShuttingDownOnIOThread(false)
, mChannel(0)
, mAuth(false)
@ -105,7 +89,6 @@ public:
int aChannel, bool aAuth, bool aEncrypt)
: ipc::UnixFdWatcher(aIOLoop)
, mConsumer(aConsumer)
, mReadMsgForClientFd(false)
, mShuttingDownOnIOThread(false)
, mChannel(aChannel)
, mAuth(aAuth)
@ -118,7 +101,6 @@ public:
int aChannel, bool aAuth, bool aEncrypt)
: ipc::UnixFdWatcher(aIOLoop)
, mConsumer(aConsumer)
, mReadMsgForClientFd(false)
, mShuttingDownOnIOThread(false)
, mDeviceAddress(aDeviceAddress)
, mChannel(aChannel)
@ -191,11 +173,6 @@ public:
*/
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
@ -218,15 +195,6 @@ private:
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.
*/
@ -580,60 +548,6 @@ DroidSocketImpl::Accept(int aFd)
}
}
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)
{
@ -872,7 +786,6 @@ BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
, mImpl(nullptr)
, mAuth(aAuth)
, mEncrypt(aEncrypt)
, mReceivedSocketInfoLength(0)
{
MOZ_ASSERT(aObserver);
@ -936,7 +849,6 @@ 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);
@ -988,7 +900,6 @@ BluetoothSocket::Listen(int aChannel)
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_FALSE(mImpl, false);
mIsServer = true;
mImpl = new DroidSocketImpl(XRE_GetIOMessageLoop(), this, aChannel, mAuth,
mEncrypt);
@ -1014,55 +925,6 @@ BluetoothSocket::SendDroidSocketData(UnixSocketRawData* 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)
{
@ -1094,4 +956,3 @@ BluetoothSocket::OnDisconnect()
MOZ_ASSERT(mObserver);
mObserver->OnSocketDisconnect(this);
}

View File

@ -57,10 +57,6 @@ private:
nsString mDeviceAddress;
bool mAuth;
bool mEncrypt;
bool mIsServer;
int mReceivedSocketInfoLength;
bool ReceiveSocketInfo(nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage);
};
END_BLUETOOTH_NAMESPACE