Bug 915533 - Patch 2/2: [bludroid OPP] BluetoothSocket, r=echou

This commit is contained in:
Ben Tian 2013-12-02 10:39:58 +08:00
parent 1d917ad645
commit 8472e8db0c
6 changed files with 813 additions and 161 deletions

View File

@ -158,11 +158,7 @@ public:
void Run() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
if (mSocket->GetConnectionStatus() ==
SocketConnectionStatus::SOCKET_CONNECTED) {
mSocket->Disconnect();
}
mSocket->CloseDroidSocket();
}
private:
@ -189,7 +185,7 @@ BluetoothOppManager::BluetoothOppManager() : mConnected(false)
, mWaitingToSendPutFinal(false)
, mCurrentBlobIndex(-1)
{
mConnectedDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
}
BluetoothOppManager::~BluetoothOppManager()
@ -211,7 +207,14 @@ BluetoothOppManager::Init()
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;
}
@ -244,14 +247,9 @@ 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;
@ -262,18 +260,9 @@ BluetoothOppManager::ConnectInternal(const nsAString& aDeviceAddress)
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
@ -286,13 +275,9 @@ BluetoothOppManager::HandleShutdown()
mSocket->Disconnect();
mSocket = nullptr;
}
if (mRfcommSocket) {
mRfcommSocket->Disconnect();
mRfcommSocket = nullptr;
}
if (mL2capSocket) {
mL2capSocket->Disconnect();
mL2capSocket = nullptr;
if (mServerSocket) {
mServerSocket->Disconnect();
mServerSocket = nullptr;
}
sBluetoothOppManager = nullptr;
}
@ -307,28 +292,23 @@ BluetoothOppManager::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;
@ -1250,7 +1230,7 @@ BluetoothOppManager::SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize)
UnixSocketRawData* s = new UnixSocketRawData(aSize);
memcpy(s->mData, aData, s->mSize);
mSocket->SendSocketData(s);
mSocket->SendDroidSocketData(s);
}
void
@ -1266,7 +1246,7 @@ BluetoothOppManager::FileTransferComplete()
type.AssignLiteral("bluetooth-opp-transfer-complete");
name.AssignLiteral("address");
v = mConnectedDeviceAddress;
v = mDeviceAddress;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("success");
@ -1306,7 +1286,7 @@ BluetoothOppManager::StartFileTransfer()
type.AssignLiteral("bluetooth-opp-transfer-start");
name.AssignLiteral("address");
v = mConnectedDeviceAddress;
v = mDeviceAddress;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("received");
@ -1342,7 +1322,7 @@ BluetoothOppManager::UpdateProgress()
type.AssignLiteral("bluetooth-opp-update-progress");
name.AssignLiteral("address");
v = mConnectedDeviceAddress;
v = mDeviceAddress;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("received");
@ -1372,7 +1352,7 @@ BluetoothOppManager::ReceivingFileConfirmation()
type.AssignLiteral("bluetooth-opp-receiving-file-confirmation");
name.AssignLiteral("address");
v = mConnectedDeviceAddress;
v = mDeviceAddress;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("fileName");
@ -1412,28 +1392,19 @@ BluetoothOppManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
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) {
@ -1446,8 +1417,7 @@ BluetoothOppManager::OnSocketConnectError(BluetoothSocket* aSocket)
{
BT_LOGR("%s: [%s]", __FUNCTION__, (mIsServer)? "server" : "client");
mRfcommSocket = nullptr;
mL2capSocket = nullptr;
mServerSocket = nullptr;
mSocket = nullptr;
if (!mIsServer) {
@ -1464,13 +1434,12 @@ BluetoothOppManager::OnSocketConnectError(BluetoothSocket* aSocket)
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
@ -1492,7 +1461,7 @@ BluetoothOppManager::OnSocketDisconnect(BluetoothSocket* aSocket)
}
AfterOppDisconnected();
mConnectedDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
mSuccessFlag = false;
mSocket = nullptr;
@ -1502,50 +1471,6 @@ BluetoothOppManager::OnSocketDisconnect(BluetoothSocket* aSocket)
}
}
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
@ -1561,6 +1486,20 @@ BluetoothOppManager::AcquireSdcardMountLock()
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)

View File

@ -122,7 +122,7 @@ private:
* Set when OBEX session is established.
*/
bool mConnected;
nsString mConnectedDeviceAddress;
nsString mDeviceAddress;
/**
* Remote information
@ -212,15 +212,13 @@ private:
// 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

View File

@ -6,60 +6,692 @@
#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);
if (!ListenSocket(c.forget())) {
nsAutoString addr;
GetAddress(addr);
BT_LOGD("%s failed. Current connected device address: %s",
__FUNCTION__, NS_ConvertUTF16toUTF8(addr).get());
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);
}
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;
}
@ -67,6 +699,10 @@ BluetoothSocket::Listen(int aChannel)
void
BluetoothSocket::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage)
{
if (ReceiveSocketInfo(aMessage)) {
return;
}
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mObserver);
mObserver->ReceiveSocketData(this, aMessage);

View File

@ -13,6 +13,7 @@
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothSocketObserver;
class DroidSocketImpl;
class BluetoothSocket : public mozilla::ipc::UnixSocketConsumer
{
@ -22,11 +23,35 @@ public:
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;
@ -37,14 +62,24 @@ public:
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

View File

@ -20,8 +20,9 @@
#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"
@ -115,6 +116,12 @@ public:
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;
}
};
@ -1290,14 +1297,45 @@ BluetoothServiceBluedroid::SendFile(const nsAString& aDeviceAddress,
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
@ -1305,7 +1343,19 @@ 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

View File

@ -2971,10 +2971,8 @@ BluetoothDBusService::SendFile(const nsAString& aDeviceAddress,
// 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");
}
@ -2992,10 +2990,8 @@ BluetoothDBusService::StopSendingFile(const nsAString& aDeviceAddress,
// 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");
}
@ -3007,17 +3003,15 @@ 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");
}